web-dev-qa-db-fra.com

Comment définir Singleton dans TypeScript

Quel est le meilleur moyen et le plus pratique d'implémenter un modèle Singleton pour une classe dans TypeScript? (Avec et sans initialisation lente).

94
maja

Les classes singleton dans TypeScript sont généralement des anti-modèles. Vous pouvez simplement utiliser des espaces de noms à la place.

Motif singleton inutile

class Singleton {
    /* ... lots of singleton logic ... */
    public someMethod() { ... }
}

// Using
var x = Singleton.getInstance();
x.someMethod();

Équivalent d'espace de noms

namespace Singleton {
    export function someMethod() { ... }
}
// Usage
Singleton.someMethod();
var x = Singleton; // If you need to alias it for some reason
72
Ryan Cavanaugh

Depuis TS 2.0, nous avons la possibilité de définir des modificateurs de visibilité sur les constructeurs . Nous pouvons donc désormais créer des singletons en TypeScript comme nous en avons l'habitude dans d'autres langages.

Exemple donné:

class MyClass
{
    private static _instance: MyClass;

    private constructor()
    {
        //...
    }

    public static get Instance()
    {
        // Do you need arguments? Make it a regular method instead.
        return this._instance || (this._instance = new this());
    }
}

const myClassInstance = MyClass.Instance;
124
Alex

Le meilleur moyen que j'ai trouvé est:

class SingletonClass {

    private static _instance:SingletonClass = new SingletonClass();

    private _score:number = 0;

    constructor() {
        if(SingletonClass._instance){
            throw new Error("Error: Instantiation failed: Use SingletonClass.getInstance() instead of new.");
        }
        SingletonClass._instance = this;
    }

    public static getInstance():SingletonClass
    {
        return SingletonClass._instance;
    }

    public setScore(value:number):void
    {
        this._score = value;
    }

    public getScore():number
    {
        return this._score;
    }

    public addPoints(value:number):void
    {
        this._score += value;
    }

    public removePoints(value:number):void
    {
        this._score -= value;
    }

}

Voici comment vous l'utilisez:

var scoreManager = SingletonClass.getInstance();
scoreManager.setScore(10);
scoreManager.addPoints(1);
scoreManager.removePoints(2);
console.log( scoreManager.getScore() );

http://www.codebelt.com/TypeScript/typescript-singleton-pattern/

36
codeBelt

L’approche suivante crée une classe Singleton qui peut être utilisée exactement comme une classe conventionnelle:

class Singleton {
    private static instance: Singleton;
    //Assign "new Singleton()" here to avoid lazy initialisation

    constructor() {
        if (Singleton.instance) {
            return Singleton.instance;
        }

        this. member = 0;
        Singleton.instance = this;
    }

    member: number;
}

Chaque opération new Singleton() renverra la même instance. Cela peut toutefois être inattendu par l'utilisateur.

L'exemple suivant est plus transparent pour l'utilisateur mais nécessite une utilisation différente:

class Singleton {
    private static instance: Singleton;
    //Assign "new Singleton()" here to avoid lazy initialisation

    constructor() {
        if (Singleton.instance) {
            throw new Error("Error - use Singleton.getInstance()");
        }
        this.member = 0;
    }

    static getInstance(): Singleton {
        Singleton.instance = Singleton.instance || new Singleton();
        return Singleton.instance;
    }

    member: number;
}

Usage: var obj = Singleton.getInstance();

15
maja

Je suis surpris de ne pas voir le motif suivant ici, qui semble en réalité très simple.

// shout.ts
class ShoutSingleton {
  helloWorld() { return 'hi'; }
}

export let Shout = new ShoutSingleton();

Usage

import { Shout } from './shout';
Shout.helloWorld();
14
Romain Bruckert

Vous pouvez utiliser des expressions de classe pour cela (à partir de 1.6 je crois).

var x = new (class {
    /* ... lots of singleton logic ... */
    public someMethod() { ... }
})();

ou avec le nom si votre classe a besoin d'accéder à son type en interne

var x = new (class Singleton {
    /* ... lots of singleton logic ... */
    public someMethod(): Singleton { ... }
})();

Une autre option consiste à utiliser une classe locale dans votre singleton à l'aide de membres statiques.

class Singleton {

    private static _instance;
    public static get instance() {

        class InternalSingleton {
            someMethod() { }

            //more singleton logic
        }

        if(!Singleton._instance) {
            Singleton._instance = new InternalSingleton();
        }

        return <InternalSingleton>Singleton._instance;
    }
}

var x = Singleton.instance;
x.someMethod();
6
bingles

Ajoutez les 6 lignes suivantes à n'importe quelle classe pour en faire "Singleton" . Utilisez Alex answer si vous préférez obtenir l'instance par le biais d'une propriété plutôt que par une méthode.

class MySingleton
{
    private constructor(){ /* ... */}
    private static _instance:MySingleton;
    public static getInstance():MySingleton
    {
        return this._instance||(this._instance = new this());
    };
}

Exemple de test:

var test = MySingleton.getInstance(); // will create the first instance
var test2 = MySingleton.getInstance(); // will return the first instance
alert(test === test2); // true
4
Flavien Volken

Vous pouvez également utiliser la fonction Object.Freeze () . C'est simple et facile: 

class Singleton {

  instance: any = null;
  data: any = {} // store data in here

  constructor() {
    if (!this.instance) {
      this.instance = this;
    }
  }
}

const singleton: Singleton = new Singleton();
Object.freeze(singleton);

export default singleton;
2
kenny

Dans TypeScript, il n'est pas nécessaire de suivre la méthodologie new instance() Singleton. Une classe statique importée, sans constructeur, peut également fonctionner.

Considérer:

export class YourSingleton {

   public static foo:bar;

   public static initialise(_initVars:any):void {
     YourSingleton.foo = _initvars.foo;
   }

   public static doThing():bar {
     return YourSingleton.foo
   }
}

Vous pouvez importer la classe et faire référence à YourSingleton.doThing() dans toute autre classe. Mais rappelez-vous, comme il s'agit d'une classe statique, elle n'a pas de constructeur. J'utilise donc généralement une méthode intialise() appelée à partir d'une classe qui importe le Singleton:

import {YourSingleton} from 'singleton.ts';

YourSingleton.initialise(params);
let _result:bar = YourSingleton.doThing();

N'oubliez pas que dans une classe statique, chaque méthode et variable doit également être statique. Par conséquent, au lieu de this, vous utiliseriez le nom de classe complet YourSingleton.

1
Dominic Lee

Ma solution pour cela:

export default class Modal {
    private static _instance : Modal = new Modal();

    constructor () {
        if (Modal._instance) 
            throw new Error("Use Modal.instance");
        Modal._instance = this;
    }

    static get instance () {
        return Modal._instance;
    }
}

je pense que peut-être utiliser des génériques être pâte

class Singleton<T>{
    public static Instance<T>(c: {new(): T; }) : T{
        if (this._instance == null){
            this._instance = new c();
        }
        return this._instance;
    }

    private static _instance = null;
}

comment utiliser

étape 1

class MapManager extends Singleton<MapManager>{
     //do something
     public init():void{ //do }
}

étape 2

    MapManager.Instance(MapManager).init();
1
sanye

C'est probablement le processus le plus long pour créer un singleton dans TypeScript, mais dans les applications plus grandes, c'est celui qui a le mieux fonctionné pour moi.

Tout d’abord, vous avez besoin d’une classe Singleton dans, disons, "./utils/Singleton.ts" :

module utils {
    export class Singleton {
        private _initialized: boolean;

        private _setSingleton(): void {
            if (this._initialized) throw Error('Singleton is already initialized.');
            this._initialized = true;
        }

        get setSingleton() { return this._setSingleton; }
    }
}

Maintenant, imaginez que vous ayez besoin d’un routeur singleton "./navigation/Router.ts" :

/// <reference path="../utils/Singleton.ts" />

module navigation {
    class RouterClass extends utils.Singleton {
        // NOTICE RouterClass extends from utils.Singleton
        // and that it isn't exportable.

        private _init(): void {
            // This method will be your "construtor" now,
            // to avoid double initialization, don't forget
            // the parent class setSingleton method!.
            this.setSingleton();

            // Initialization stuff.
        }

        // Expose _init method.
        get init { return this.init; }
    }

    // THIS IS IT!! Export a new RouterClass, that no
    // one can instantiate ever again!.
    export var Router: RouterClass = new RouterClass();
}

Nice !, initialisez ou importez où vous voulez:

/// <reference path="./navigation/Router.ts" />

import router = navigation.Router;

router.init();
router.init(); // Throws error!.

La bonne chose à faire de singletons de cette façon est que vous utilisez toujours toute la beauté des classes TypeScript, cela vous donne Nice intellisense, la logique du singleton reste en quelque sorte séparée et il est facile de la supprimer si nécessaire.

1
a.guerrero.g87
class MySingleton {
constructor(message) {
    alert('hello' + message);
}
private static instance: MySingleton;
private static instance2: MySingleton;

public static getSessionStorageInstance() {
    if (!MySingleton.instance) {
        MySingleton.instance = new MySingleton("world");
    }
    return MySingleton.instance;
}

public static getSessionStorageInstance2() {
    if (!MySingleton.instance2) {
        MySingleton.instance2 = new MySingleton("youu");
    }
    return MySingleton.instance2;
 }
}

 const a: MySingleton = MySingleton.getSessionStorageInstance();
 const b: MySingleton = MySingleton.getSessionStorageInstance2();

 alert(a === b)
0
lahbib

Ce n'est pas un simple singleton (l'initialisation n'est peut-être pas paresseuse), mais un modèle similaire avec l'aide de namespaces.

namespace MyClass
{
    class _MyClass
    {
    ...
    }
    export const instance: _MyClass = new _MyClass();
}

Accès à l'objet de Singleton:

MyClass.instance
0
sergzach

Voici encore un autre moyen de le faire avec une approche javascript plus conventionnelle utilisant un IFFE :

module App.Counter {
    export var Instance = (() => {
        var i = 0;
        return {
            increment: (): void => {
                i++;
            },
            getCount: (): number => {
                return i;
            }
        }
    })();
}

module App {
    export function countStuff() {
        App.Counter.Instance.increment();
        App.Counter.Instance.increment();
        alert(App.Counter.Instance.getCount());
    }
}

App.countStuff();

Voir une démo

0
JesperA

Une autre option consiste à utiliser des symboles dans votre module. De cette façon, vous pouvez protéger votre classe, même si l'utilisateur final de votre API utilise du Javascript normal:

let _instance = Symbol();
export default class Singleton {

    constructor(singletonToken) {
        if (singletonToken !== _instance) {
            throw new Error("Cannot instantiate directly.");
        }
        //Init your class
    }

    static get instance() {
        return this[_instance] || (this[_instance] = new Singleton(_singleton))
    }

    public myMethod():string {
        return "foo";
    }
}

Usage:

var str:string = Singleton.instance.myFoo();

Si l'utilisateur utilise votre fichier js d'API compilé, il recevra également une erreur s'il tente d'instancier manuellement votre classe:

// PLAIN JAVASCRIPT: 
var instance = new Singleton(); //Error the argument singletonToken !== _instance symbol
0
Ciberman

C'est le moyen le plus simple

class YourSingletoneClass {
  private static instance: YourSingletoneClass;

  private constructor(public ifYouHaveAnyParams: string) {

  }
  static getInstance() {
    if(!YourSingletoneClass.instance) {
      YourSingletoneClass.instance = new YourSingletoneClass('If you have any params');
    }
    return YourSingletoneClass.instance;
  }
}
0
Sorin Veștemean