Frage So definieren Sie Singleton in TypeScript


Was ist die beste und bequemste Möglichkeit, ein Singleton-Muster für eine Klasse in TypeScript zu implementieren? (Sowohl mit als auch ohne faule Initialisierung).


75
2018-05-11 17:35


Ursprung


Antworten:


Singleton-Klassen in TypeScript sind im Allgemeinen ein Anti-Pattern. Sie können stattdessen einfach Namespaces verwenden.

Nutzloses Singleton-Muster

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

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

Namensraum entspricht

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

64
2018-05-11 17:50



Seit TS 2.0 haben wir die Möglichkeit zu definieren Sichtbarkeitsmodifikatoren für KonstruktorenJetzt können wir also in TypeScript Singletons erstellen, so wie wir es aus anderen Sprachen gewohnt sind.

Beispiel gegeben:

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;

82
2018-05-02 08:42



Der beste Weg, den ich gefunden habe, ist:

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;
    }

}

Hier ist, wie Sie es verwenden:

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/


31
2017-07-01 06:47



Der folgende Ansatz erstellt eine Singleton-Klasse, die genau wie eine konventionelle Klasse verwendet werden kann:

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;
}

Jeder new Singleton() Die Operation gibt dieselbe Instanz zurück. Dies kann jedoch für den Benutzer unerwartet sein.

Das folgende Beispiel ist für den Benutzer transparenter, erfordert jedoch eine andere Verwendung:

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;
}

Verwendung: var obj = Singleton.getInstance();


12
2018-05-11 17:35



Ich bin überrascht, hier nicht das folgende Muster zu sehen, das wirklich sehr einfach aussieht.

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

export let Shout = new ShoutSingleton();

Verwendung

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

9
2018-04-28 07:38



Sie können dafür Klassenausdrücke verwenden (ab 1.6 glaube ich).

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

oder mit dem Namen, wenn Ihre Klasse intern auf ihren Typ zugreifen muss

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

Eine andere Option ist die Verwendung einer lokalen Klasse in Ihrem Singleton mithilfe einiger statischer Member

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
2017-11-09 19:41



Fügen Sie jeder Klasse die folgenden 6 Zeilen hinzu, um sie zu "Singleton" zu machen. Verwenden Sie Alex antwort, wenn Sie die Instanz lieber über eine Eigenschaft als eine Methode erhalten möchten.

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

Testbeispiel:

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

2
2018-02-14 10:49



Dies ist wahrscheinlich der längste Prozess, um einen Singleton in Typoskript zu erstellen, aber in größeren Anwendungen ist derjenige, der für mich besser funktioniert hat.

Zuerst benötigen Sie eine Singleton-Klasse in, sagen wir, "./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; }
    }
}

Stellen Sie sich vor, Sie benötigen einen Router 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();
}

Nett !, jetzt initialisieren oder importieren, wo immer Sie wollen:

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

import router = navigation.Router;

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

Das Schöne daran, Singletons auf diese Weise zu machen, ist, dass Sie immer noch die Schönheit von Typoskript-Klassen verwenden, es gibt Ihnen nette Intelligenz, die Singleton-Logik bleibt irgendwie getrennt und es ist leicht zu entfernen, wenn nötig.


1
2017-07-21 19:06



Hier ist noch eine andere Möglichkeit, es mit einem konventionellen Javascript-Ansatz mit einem zu tun 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();

Sehen Sie sich an Demo


0
2017-11-11 19:41



Eine weitere Option ist die Verwendung von Symbolen in Ihrem Modul. Auf diese Weise können Sie Ihre Klasse schützen, auch wenn der Endbenutzer Ihrer API normales Javascript verwendet:

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";
    }
}

Verwendung:

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

Wenn der Benutzer Ihre kompilierte API js-Datei verwendet, wird auch ein Fehler angezeigt, wenn er versucht, Ihre Klasse manuell zu instanziieren:

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

0
2017-08-09 06:00