在Typescript / ES6中,Singleton模式的正确方法是什么?

时间:2016-10-15 16:01:05

标签: javascript typescript ecmascript-6

class Foo{

}

var instance: Foo;
export function getFooInstance(){
    /* logic */
}

export class Foo{
    private static _instance;
    private constructor(){};
    public getInstance(){/* logic */}
}

// Use it like this
Foo.getInstance()

我想确保对象只有一个方法实例?除此之外还有其他任何建议吗?

两者的打字稿游乐场link

3 个答案:

答案 0 :(得分:5)

如果你想在课堂上使用getter,那么它需要是静态的:

export class Foo{
    private static _instance;
    private constructor(){};
    public static getInstance(){/* logic */}
}

问题在于,虽然编译器会强制执行此私有可见性,但在运行时仍然可以绕过它,甚至是无意中,例如有人直接使用javascript。

如果使用模块/命名空间强制执行,则可以完全隐藏它:

使用模块:

export interface IFoo {}

class Foo implements IFoo {}

var instance: Foo;
export function getFooInstance(): IFoo {
    /* logic */

    return instance;
}

这是你的代码,我只是对IFoo接口(也是导出的)进行了修改,以便谁获得一个实例将知道接口而不是类。

使用命名空间:

namespace Foo {
    export interface IFoo {}

    class FooClass implements IFoo {}

    const instance = new FooClass();
    export function getInstance(): IFoo {
        return instance;
    }
}

答案 1 :(得分:4)

在JS和TypeScript中,如果你真的只想要一个实例,那么为什么不通过导出一个对象文字来强制执行语言呢?

const Foo = {
  doSomething() {

  }
}

export default Foo;

IMO,这是遵循KISS,最少量的样板,并且任何人都无法创建多个实例。

话虽如此,你也可以直接导出函数。请记住,模块本身可以作为单身人士。

export function doSomething() {
}

然后导入并希望将其视为可以使用import *的对象。如果函数真的属于对象而不是所有无状态静态函数,我更喜欢第一种方法。

import * as Foo from './Foo';

Foo.doSomething();

答案 2 :(得分:2)

这取决于是否应该有机会为单例类创建新实例。在最后一种情况下,getInstance可以省略,类构造函数可以作为单例工厂:

class Foo {
    private static _instance;
    constructor(...args) {
        if (Foo._instance) {
            return Foo._instance;
        }

        // if Foo extends non-singleton class or whatever,
        // super(...args);

        Foo._instance = this;
    };
}

任何类的装饰器都可以完成同样的事情,例如:

@singleton
class Foo { ... }

由于存在一些typing problems with TypeScript decorators,因此应在singleton装饰器中使用自定义继承代码而不是Singleton extends Class

function singleton(Class) {
    function extend(sub, sup) {

        for (var prop in sup)
            if (sup.hasOwnProperty(prop))
                sub[prop] = sup[prop];

        function __() {
            this.constructor = sub;
        }

        __.prototype = sup.prototype;
        sub.prototype = new __();
    };

    const Singleton = <any>function (...args) {
        if (Singleton._instance) {
            return Singleton._instance;
        }

        Class.apply(this, args);

        Singleton._instance = this;
    }

    extend(Singleton, Class);

    return Singleton;
}

这可能会影响打字,但语法仍然很整洁。