我有一个 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
时,他或她无法知道传递的选项对象将被使用或被忽略。
最佳答案
在 TypeScript 中,就像在 JavaScript 中一样,单例模式并不存在,正如您可能从 Java 或其他语言中了解到的那样。
为什么我们没有相同的概念?
因为对象和类不相互依赖。
首先让我们问问自己什么是单例?
在整个应用程序中全局只能有一个实例的类。
在 TypeScript 中,您可以通过创建全局变量来实现。
例如:
// at global scope
var instance = {
prodMode: true
};
或在模块内:
globalThis.instance = {
prodMode: true
};
declare global {
var instance: {
prodMode: boolean
};
}
就是这样,没有类,没有锁或守卫,只有一个全局变量。
上述方法具有以下额外优势(就在我的脑海中):
- 根据定义是单例
- 非常简单。
- 该对象使用起来方便、随意。
现在您可以回应您需要或至少想要一个类。
没问题:
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
)的方法具有以下优点:
- 无需编写
私有(private)构造函数
来防止外部实例化。 - 该类不能直接实例化(尽管它可以作为
instance.constructor
访问,这是避免使用类并使用前面所述的简单对象的另一个原因)。 - 没有
class
可以扩展或以任何方式滥用,这会在运行时破坏单例模式(尽管它可以作为instance.constructor
访问,这是如前所述,避免使用类并使用简单对象的另一个原因。
但正如您所说,将选项传递给 getInstance
函数会使 API 变得不清楚。
如果意图是允许更改实例,只需公开一个重新配置方法(或只是使对象可变)。
globalThis.instance = {
prodMode: true,
reconfigure(options) {
this.prodMode = options.prodMode;
}
};
备注:
JavaScript 中一些众所周知的单例示例包括
- 对象对象
- 数组对象
- 函数对象
- (你最喜欢的库)当通过脚本标签加载时
全局变量通常很糟糕,而可变全局变量通常更糟。
模块可以提供帮助。
就个人而言,因为我使用模块,如果我想要一个不同的全局对象,这取决于诸如“正在生产”之类的东西的值(value),我会写
// app.ts
export {}
(async function run() {
const singletonConditionalModuleSpecifer = prodMode
? "./prod-singleton"
: "./dev-singleton";
const singleton = await import(singletonConditionalModuleSpecifer);
// use singleton
}());
关于javascript - 在创建单例实例时传递选项的 TypeScript 模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49543310/