在TypeScript中为类实现Singleton模式的最佳和最方便的方法是什么? (有和没有延迟初始化)。
TypeScript中的Singleton类通常是反模式。您可以简单地使用命名空间。
class Singleton {
/* ... lots of singleton logic ... */
public someMethod() { ... }
}
// Using
var x = Singleton.getInstance();
x.someMethod();
export namespace Singleton {
export function someMethod() { ... }
}
// Usage
import { SingletonInstance} from "path/to/Singleton";
SingletonInstance.someMethod();
var x = SingletonInstance; // If you need to alias it for some reason
我的解决方案:
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;
}
}
在Typescript中,不一定要遵循new instance()
Singleton方法。导入的无构造函数的静态类也可以同样工作。
考虑:
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()
。但请记住,因为这是一个静态类,它没有构造函数所以我通常使用从导入Singleton的类调用的intialise()
方法:
import {YourSingleton} from 'singleton.ts';
YourSingleton.initialise(params);
let _result:bar = YourSingleton.doThing();
不要忘记在静态类中,每个方法和变量也需要是静态的,所以不使用this
而是使用完整的类名YourSingleton
。
我想也许使用泛型是面糊
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;
}
如何使用
步骤1
class MapManager extends Singleton<MapManager>{
//do something
public init():void{ //do }
}
第2步
MapManager.Instance(MapManager).init();
这是使用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();
查看demo
另一种选择是在模块中使用符号。这样,您可以保护您的课程,如果您的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
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)
不是纯粹的单身人士(初始化可能不是懒惰的),而是在namespace
s的帮助下的类似模式。
namespace MyClass
{
class _MyClass
{
...
}
export const instance: _MyClass = new _MyClass();
}
访问Singleton的对象:
MyClass.instance
这是最简单的方法
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;
}
}
namespace MySingleton {
interface IMySingleton {
doSomething(): void;
}
class MySingleton implements IMySingleton {
private usePrivate() { }
doSomething() {
this.usePrivate();
}
}
export var Instance: IMySingleton = new MySingleton();
}
通过这种方式,我们可以应用界面,这与Ryan Cavanaugh接受的答案不同。
从TS 2.0开始,我们就能够定义visibility modifiers on constructors,所以现在我们可以像在其他语言中习惯的那样在TypeScript中做单例。
给出的例子:
class MyClass
{
private static _instance: MyClass;
private constructor()
{
//...
}
public static get Instance()
{
// Do you need arguments? Make it a regular static method instead.
return this._instance || (this._instance = new this());
}
}
const myClassInstance = MyClass.Instance;
感谢@Drenai指出如果使用原始编译的javascript编写代码,则无法防止多个实例化,因为TS的约束消失并且构造函数不会被隐藏。
我发现的最好方法是:
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/
以下方法创建一个Singleton类,可以像传统类一样使用:
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行添加到任何类中以使其成为“Singleton”。如果您希望通过属性而不是方法获取实例,请使用Alex回答。
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
您还可以使用Object.Freeze()函数。它简单易行:
class Singleton {
instance: any = null;
data: any = {} // store data in here
constructor() {
if (!this.instance) {
this.instance = this;
}
return this.instance
}
}
const singleton: Singleton = new Singleton();
Object.freeze(singleton);
export default singleton;
这可能是在打字稿中制作单例的最长过程,但在较大的应用程序中,这对我来说效果更好。
首先,你需要一个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!.
以这种方式做单身的好处是你仍然使用所有的打字稿类的美丽,它给你很好的智能感知,单身逻辑保持分离,如果需要它很容易删除。