Pregunta Cómo definir Singleton en TypeScript


¿Cuál es la mejor y más conveniente forma de implementar un patrón de Singleton para una clase en TypeScript? (Ambos con y sin inicialización diferida).


75
2018-05-11 17:35


origen


Respuestas:


Las clases Singleton en TypeScript generalmente son un antipatrón. Simplemente puede usar espacios de nombres en su lugar.

Patrón de singleton inútil

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

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

Espacio de nombres equivalente

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



Desde TS 2.0, tenemos la capacidad de definir modificadores de visibilidad en constructores, entonces ahora podemos hacer singletons en TypeScript tal como estamos acostumbrados en otros lenguajes.

Ejemplo dado:

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



La mejor manera que he encontrado es:

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

}

Aquí es cómo lo usa:

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



El siguiente enfoque crea una clase Singleton que se puede usar exactamente como una clase convencional:

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

Cada new Singleton() la operación devolverá la misma instancia. Sin embargo, esto puede ser inesperado por el usuario.

El siguiente ejemplo es más transparente para el usuario pero requiere un uso diferente:

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

Uso: var obj = Singleton.getInstance();


12
2018-05-11 17:35



Estoy sorprendido de no ver el siguiente patrón aquí, que en realidad parece muy simple.

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

export let Shout = new ShoutSingleton();

Uso

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

9
2018-04-28 07:38



Puedes usar expresiones de clase para esto (a partir de 1.6, creo).

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

o con el nombre si su clase necesita acceder a su tipo internamente

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

Otra opción es usar una clase local dentro de tu singleton usando algunos miembros estáticos

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



Agregue las siguientes 6 líneas a cualquier clase para que sea "Singleton". Utilice la respuesta de Alex si prefiere obtener la instancia a través de una propiedad en lugar de un método.

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

Ejemplo de prueba:

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



Este es probablemente el proceso más largo para hacer un singleton en texto mecanografiado, pero en aplicaciones más grandes es el que mejor me ha funcionado.

Primero necesitas una clase de Singleton, digamos, "./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; }
    }
}

Ahora imagine que necesita un enrutador 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();
}

¡Agradable !, ahora inicialízate o importa donde necesites:

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

import router = navigation.Router;

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

Lo bueno de hacer singletons de esta manera es que todavía usas toda la belleza de las clases mecanografiadas, te da una buena inteligencia, la lógica singleton se mantiene separada de alguna manera y es fácil de eliminar si es necesario.


1
2017-07-21 19:06



Aquí hay otra forma de hacerlo con un enfoque javascript más convencional usando 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();

Ver un manifestación


0
2017-11-11 19:41