Как определить синглтон в TypeScript
каков наилучший и наиболее удобный способ реализации Одноэлементного шаблона для класса в TypeScript? (Как с ленивой инициализацией, так и без нее).
14 ответов:
Singleton-классы в TypeScript, как правило, является анти-паттерн. Вы можете просто использовать пространства имен.
бесполезный одноэлементный шаблон
class Singleton { /* ... lots of singleton logic ... */ public someMethod() { ... } } // Using var x = Singleton.getInstance(); x.someMethod();
эквивалент пространства имен
namespace Singleton { export function someMethod() { ... } } // Usage Singleton.someMethod(); var x = Singleton; // If you need to alias it for some reason
начиная с TS 2.0, у нас есть возможность определить модификаторы видимости на конструкторы, Так что теперь мы можем делать синглеты в TypeScript так же, как мы привыкли с других языков.
пример:
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;
лучший способ, который я нашел:
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; } }
вот как вы его используете:
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/
следующий подход создает одноэлементный класс, который можно использовать точно так же, как обычный класс:
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; }
каждого
new Singleton()
операция возвращает тот же экземпляр. Однако это может быть неожиданным для пользователя.следующий пример более прозрачен для пользователя, но требует другого использования:
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; }
использование:
var obj = Singleton.getInstance();
Я удивлен, что не вижу здесь следующую картину, которая на самом деле выглядит очень просто.
// shout.ts class ShoutSingleton { helloWorld() { return 'hi'; } } export let Shout = new ShoutSingleton();
использование
import { Shout } from './shout'; Shout.helloWorld();
вы можете использовать выражения класса для этого (по состоянию на 1.6 я считаю).
var x = new (class { /* ... lots of singleton logic ... */ public someMethod() { ... } })();
или с именем если ваш класс должен получить доступ к его типу внутренне
var x = new (class Singleton { /* ... lots of singleton logic ... */ public someMethod(): Singleton { ... } })();
другой вариант-использовать локальный класс внутри вашего синглтона, используя некоторые статические члены
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 строк в любой класс, чтобы сделать его "Одноэлементным". Используйте Alex answer, если вы предпочитаете получать экземпляр через свойство, а не метод.
class MySingleton { private constructor(){ /* ... */} private static _instance:MySingleton; public static getInstance():MySingleton { return this._instance||(this._instance = new this()); }; }
var test = MySingleton.getInstance(); // will create the first instance var test2 = MySingleton.getInstance(); // will return the first instance alert(test === test2); // true
Это, вероятно, самый длинный процесс, чтобы сделать синглтон в typescript, но в больших приложениях это тот, который работал лучше для меня.
сначала вам нужен одноэлементный класс, скажем,"./ utils / Singleton.ТС":
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; } } }
Теперь представьте, что вам нужен маршрутизатор singleton "./ навигация / маршрутизатор.ТС":
/// <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(); }
приятно!, теперь инициализируйте или импортируйте везде, где вам нужно:
/// <reference path="./navigation/Router.ts" /> import router = navigation.Router; router.init(); router.init(); // Throws error!.
хорошая вещь о делаете синглтоны таким образом, вы все еще используете всю красоту классов typescript, это дает вам хороший intellisense, логика синглтона каким-то образом отделена, и ее легко удалить, если это необходимо.
мое решение для этого:
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; } }
Я думаю, может быть, использовать дженерики быть тесто
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; }
как использовать
step1
class MapManager extends Singleton<MapManager>{ //do something public init():void{ //do } }
step2
MapManager.Instance(MapManager).init();
вот еще один способ сделать это с более традиционным подходом javascript, используя 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();
посмотреть демо
другой вариант-использовать символы в вашем модуле. Таким образом, вы можете защитить свой класс, также если конечный пользователь вашего API использует обычный Javascript:
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"; } }
использование:
var str:string = Singleton.instance.myFoo();
если пользователь использует ваш скомпилированный файл API js, также будет получена ошибка, если он попытается вручную создать экземпляр вашего класса:
// PLAIN JAVASCRIPT: var instance = new Singleton(); //Error the argument singletonToken !== _instance symbol
в Typescript не обязательно следовать
new instance()
Синглтон методологии. Импортированный статический класс без конструктора также может работать одинаково.считаем:
export class YourSingleton { public static foo:bar; public static initialise(_initVars:any):void { YourSingleton.foo = _initvars.foo; } public static doThing():bar { return YourSingleton.foo } }
вы можете импортировать класс и относятся к
YourSingleton.doThing()
в любом другом классе. Но помните, поскольку это статический класс, у него нет конструктора, поэтому я обычно используюintialise()
метод, вызываемый из класса, который импортирует Синглтон:import {YourSingleton} from 'singleton.ts'; YourSingleton.initialise(params); let _result:bar = YourSingleton.doThing();
Не забывайте, что в статический класс, каждый метод и переменная также должны быть статическими, поэтому вместо
this
вы должны использовать полное имя классаYourSingleton
.
namespace MySingleton { interface IMySingleton { doSomething(): void; } class MySingleton implements IMySingleton { private usePrivate() { } doSomething() { this.usePrivate(); } } export var Instance: IMySingleton = new MySingleton(); }
таким образом, мы можем применить интерфейс, в отличие от принятого ответа Райан Кавано.