【问题标题】:TypeScript pattern to pass options while creating singleton instanceTypeScript 模式在创建单例实例时传递选项
【发布时间】:2018-09-07 15:48:09
【问题描述】:

我有一个 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 时,他或她无法知道传递的选项对象将被使用还是被忽略。

【问题讨论】:

  • This question 是在 Java 中,但总体思路是相似的。关于单例的构造函数依赖关系有一些很好的讨论。

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


【解决方案1】:

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

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

因为 objectsclasses 不是相互依赖的。

首先让我们问自己什么是单例?

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

在 TypeScript 中,您可以通过创建一个全局变量来做到这一点。

例如:

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

或从模块内:

globalThis.instance = {
  prodMode: true
};

declare global {
  var instance: {
    prodMode: boolean
  };
}

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

上述方法还有以下额外的优点(我想不到):

  1. 根据定义它是一个单例
  2. 很简单。
  3. 该对象可以方便而毫不客气地使用。

现在你可以回应你需要或至少想要一个课程。

没问题:

globalThis.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 访问,这是避免使用类并使用简单对象的另一个原因前文所述)。

但正如您所说,将选项传递给 getInstance 函数会使 API 不清楚。

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

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

备注:

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

  1. Object 对象
  2. 数组对象
  3. 函数对象
  4. (您最喜欢的库)通过脚本标签加载时

全局变量通常不好,可变全局变量通常更糟。

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

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

// app.ts
export {}

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

【讨论】:

  • 太棒了!谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-05-11
  • 2020-04-03
  • 2011-08-02
  • 2016-01-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多