TypeScript模式在创建单例实例时传递选项

时间:2018-03-28 20:09:30

标签: javascript typescript design-patterns singleton javascript-objects

我有一个TypeScript单例类

class MySingleton {
    private static _instance: MySingleton;

    private constructor();

    public static getInstance() {
        if (!MySingleton._instance) {
            MySingleton._instance = new MySingleton();
        }

        return MySingleton._instance;
    }

}

现在,我想在创建这个单例实例时传递一些选项。

例如,将实例的模式设置为生产模式的选项。

因此,可以通过让getInstance接受传播到构造函数的选项对象来实现。然后使用就像

MySingleton.getInstance({
    prodMode: true
});

这是最好的方法吗?这样做有点蠢,因为当MySingleton的消费者通过传递一个选项对象来执行getInstance时,他或她无法知道传递的选项对象将被使用或只是忽视。

1 个答案:

答案 0 :(得分:1)

在TypeScript中,就像在JavaScript中一样,单例模式不存在,因为您可能从Java或其他语言中知道它。

为什么我们没有相同的概念?

因为对象不是相互依赖的。

首先让我们自问什么是单身人士?

在整个应用程序中只能有一个全局实例的类。

在TypeScript中,您可以通过创建全局变量来实现。

例如:

// at global scope
var instance = {
  prodMode: true
};

或更明确地或来自模块内:

window.instance = {
  prodMode: true
};

declare global {
  interface Window {
    instance: {
      prodMode: boolean
    };
  }
}

没有类,没有锁或守卫,只是一个全局变量。

上述方法具有以下额外优势(仅限于我的头脑):

  1. 根据定义,它是单身人士
  2. 很简单。
  3. 该对象可以方便,毫不客气地使用。
  4. 现在您可以回复您需要或者至少想要一个班级。

    没问题

    window.instance = new class {
      prodMode = true
    };
    

    但请不要使用简单配置对象的类。

    现在讨论配置实例的用例:

    这取决于用例:

    如果你想拥有一个可配置的单件,你应该仔细考虑你的设计。

    但是,如果经过深思熟虑后,似乎有必要创建一个全局构造函数,请考虑以下调整

    namespace singleton {
      class Singleton_ {constructor(options: {}){}}
    
      export type Singleton = Singleton_;
    
      var instance: Singleton = undefined;
    
      export function getInstance(options: {}) {
        instance = instance || new Singleton_(options);
        return instance;
    }
    

    上述方法使用IIFE模式(TypeScript namespace)具有以下优点:

    1. 无需编写private constructor来阻止外部实例化。
    2. 该类无法直接实例化(尽管它可以作为instance.constructor访问,这是避免类并使用前面描述的简单对象的另一个原因。)
    3. 没有class可以以任何方式扩展或以其他方式滥用,这会在运行时破坏单例模式(尽管它可以作为instance.constructor访问,这是避免类和使用的另一个原因一个简单的对象,如前所述)。
    4. 但正如您所说,将选项传递给getInstance函数会导致API不清楚。

      如果意图是允许更改实例,只需公开一个重新配置方法(或者只是让对象变为可变)。

      window.instance = {
        prodMode: true,
        reconfigure(options) {
          this.prodMode = options.prodMode;
        }
      };
      

      <强>说明:

      JavaScript中众所周知的单例的一些例子包括

      1. 对象
      2. 数组对象
      3. 功能对象
      4. (您最喜欢的图书馆)通过脚本标记加载
      5. 全局变量通常都很糟糕,可变的全局变量通常更糟糕。

        模块可以在这里提供帮助。

        就个人而言,由于我使用模块,如果我想要一个不同的全局对象,取决于“正在生产”之类的值,我会写

        // app.ts
        export {}
        
        (async function run() {
          const singletonConditionalModuleSpecifer = prodMode
            ? "./prod-singleton"
            : "./dev-singleton";
        
          const singleton = await import(singletonConditionalModuleSpecifer);
          // use singleton
        }());