如何在TypeScript中定义Singleton
在TypeScript中实现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/
下面的方法创build一个可以象传统类一样使用的单例类:
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();
你可以使用类expression式(我相信从1.6)。
var x = new (class { /* ... lots of singleton logic ... */ public someMethod() { ... } })();
或者如果你的类需要在内部访问它的types的名称
var x = new (class Singleton { /* ... lots of singleton logic ... */ public someMethod(): Singleton { ... } })();
另一种select是使用一些静态成员在你的单例中使用本地类
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();
我很惊讶,看不到下面的模式,这实际上看起来很简单。
// shout.ts class ShoutSingleton { helloWorld() { return 'hi'; } } export let Shout = new ShoutSingleton();
用法
import { Shout } from './shout'; Shout.helloWorld();
将以下5行添加到任何类中,使其成为“Singleton”。 如果你喜欢通过属性而不是方法获取实例,请使用Alex答案。
class MySingleton { private static _instance:MySingleton; public static getInstance():MySingleton { return MySingleton._instance||(MySingleton._instance = new MySingleton()); }; }
testing例子:
var test = MySingleton.getInstance(); // will create the first instance var test2 = MySingleton.getInstance(); // will return the first instance alert(test === test2); // true
这可能是打字稿中单身人士最长的一个过程,但是在较大的应用程序中,这对我来说是一个更好的工作。
首先你需要一个Singleton类,比方说, “./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; } } }
现在想象你需要一个路由器单身“./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(); }
尼斯!,现在初始化或导入你需要的任何地方:
/// <reference path="./navigation/Router.ts" /> import router = navigation.Router; router.init(); router.init(); // Throws error!.
这样做单身人士的好处是,你仍然使用打字稿类的所有美丽,它给你很好的intellisense,单身逻辑保持分开,很容易删除,如果需要的话。
这是另一种使用IFFE的更传统的JavaScript方法:
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();
查看演示
另一种select是在模块中使用符号。 这样你可以保护你的类,如果你的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
我的解决scheme是:
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; } }
namespace MySingleton { interface IMySingleton { doSomething(): void; } class MySingleton implements IMySingleton { private usePrivate() { } doSomething() { this.usePrivate(); } } export var Instance: IMySingleton = new MySingleton(); }
这种方式我们可以应用一个接口,不像Ryan Cavanaugh公认的答案。