Typescript/ES6 中单例模式的正确方法是什么?

问题描述 投票:0回答:3
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()

我想确保该对象只有一种方式实例?除了这个还有其他建议吗?

Typescript Playground 链接

javascript typescript ecmascript-6 singleton
3个回答
8
投票

如果你想在类中使用 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;
    }
}

5
投票

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

const Foo = {
  doSomething() {

  }
}

export default Foo;

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

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

export function doSomething() {
}

然后导入并希望将其视为一个对象,您可以使用 import *.如果函数确实属于对象并且并非都是无状态静态函数,我更喜欢第一种方法。

import * as Foo from './Foo';

Foo.doSomething();

1
投票

这取决于是否有机会为单例类创建新实例。在最后一种情况下

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 { ... }

由于 TypeScript 装饰器存在一些打字问题,因此应在

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

这可能会伤害打字,但语法保持整洁。

© www.soinside.com 2019 - 2024. All rights reserved.