我有一个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
时,他或她无法知道传递的选项对象将被使用或只是忽视。
答案 0 :(得分:1)
在TypeScript中,就像在JavaScript中一样,单例模式不存在,因为您可能从Java或其他语言中知道它。
为什么我们没有相同的概念?
因为对象和类不是相互依赖的。
首先让我们自问什么是单身人士?
在整个应用程序中只能有一个全局实例的类。
在TypeScript中,您可以通过创建全局变量来实现。
例如:
// at global scope
var instance = {
prodMode: true
};
或更明确地或来自模块内:
window.instance = {
prodMode: true
};
declare global {
interface Window {
instance: {
prodMode: boolean
};
}
}
没有类,没有锁或守卫,只是一个全局变量。
上述方法具有以下额外优势(仅限于我的头脑):
现在您可以回复您需要或者至少想要一个班级。
没问题
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
)具有以下优点:
private constructor
来阻止外部实例化。instance.constructor
访问,这是避免类并使用前面描述的简单对象的另一个原因。)class
可以以任何方式扩展或以其他方式滥用,这会在运行时破坏单例模式(尽管它可以作为instance.constructor
访问,这是避免类和使用的另一个原因一个简单的对象,如前所述)。但正如您所说,将选项传递给getInstance
函数会导致API不清楚。
如果意图是允许更改实例,只需公开一个重新配置方法(或者只是让对象变为可变)。
window.instance = {
prodMode: true,
reconfigure(options) {
this.prodMode = options.prodMode;
}
};
<强>说明:强>
JavaScript中众所周知的单例的一些例子包括
全局变量通常都很糟糕,可变的全局变量通常更糟糕。
模块可以在这里提供帮助。
就个人而言,由于我使用模块,如果我想要一个不同的全局对象,取决于“正在生产”之类的值,我会写
// app.ts
export {}
(async function run() {
const singletonConditionalModuleSpecifer = prodMode
? "./prod-singleton"
: "./dev-singleton";
const singleton = await import(singletonConditionalModuleSpecifer);
// use singleton
}());