I have a TypeScript singleton class
class MySingleton {
private static _instance: MySingleton;
private constructor();
public static getInstance() {
if (!MySingleton._instance) {
MySingleton._instance = new MySingleton();
}
return MySingleton._instance;
}
}
Now, I would like to pass some options while creating this singleton instance.
E.g an option that sets the mode of the instance to production mode.
So, that could be implemented by having getInstance
accept an options object that gets propagated to the constructor. Then the usage bees like
MySingleton.getInstance({
prodMode: true
});
Is this the best way to do this? I feel kind of icky about doing this because when the consumer of MySingleton
executes getInstance
by passing an options object, there is no way for him or her to know that the passed options object will be used or just disregarded.
I have a TypeScript singleton class
class MySingleton {
private static _instance: MySingleton;
private constructor();
public static getInstance() {
if (!MySingleton._instance) {
MySingleton._instance = new MySingleton();
}
return MySingleton._instance;
}
}
Now, I would like to pass some options while creating this singleton instance.
E.g an option that sets the mode of the instance to production mode.
So, that could be implemented by having getInstance
accept an options object that gets propagated to the constructor. Then the usage bees like
MySingleton.getInstance({
prodMode: true
});
Is this the best way to do this? I feel kind of icky about doing this because when the consumer of MySingleton
executes getInstance
by passing an options object, there is no way for him or her to know that the passed options object will be used or just disregarded.
- This question is in Java, but the general idea is similar. There's some good discussion about constructor dependencies for singletons. – JJWesterkamp Commented Mar 28, 2018 at 20:16
1 Answer
Reset to default 5In TypeScript, as in JavaScript, the singleton pattern does not exist as you might know it from Java or another language.
Why don't we have the same concept?
Because objects and classes are not interdependent.
First lets ask ourselves what is a singleton?
A class that can have only one instance globally across an entire application.
In TypeScript, you would do that by creating a global variable.
For example:
// at global scope
var instance = {
prodMode: true
};
or from within a module:
globalThis.instance = {
prodMode: true
};
declare global {
var instance: {
prodMode: boolean
};
}
There it is, no classes, no locks or guards, just a global variable.
The above approach has the following additional advantages (just off the top of my head):
- It is a singleton by definition
- It is very simple.
- The object can used conveniently and unceremoniously.
Now you may respond that you need or at least want a class.
No problem:
globalThis.instance = new class {
prodMode = true
}();
But please do not use classes for simple configuration objects.
Now on to your use case of configuring the instance:
If you want to have a configurable singleton, you should consider your design carefully.
But if, after much thought, it seems necessary to create a global constructor function consider the following adjustment:
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;
}
The above approach, which uses the IIFE pattern (a TypeScript namespace
) has the following advantages:
- There is no need to write a
private constructor
to prevent external instantiation. - The class cannot be directly instantiated (although it is accessible as
instance.constructor
which is another reason to avoid a class and use a simple object as described earlier). - There is no
class
that can be extended or otherwise misused in any way which would break the singleton pattern at runtime (although it is accessible asinstance.constructor
which is another reason to avoid a class and use a simple object as described earlier).
But as you say, passing options to the getInstance
function makes the API unclear.
If the intent is rather to allow the instance to be changed, simply expose a reconfigure method (or just make the object mutable).
globalThis.instance = {
prodMode: true,
reconfigure(options) {
this.prodMode = options.prodMode;
}
};
Remarks:
Some examples of well known singletons in JavaScript include
- The Object object
- The Array object
- The Function object
- (Your favorite library) when loaded via a script tag
Globals are generally bad and mutable globals are generally worse.
Modules can help here.
Personally, since I use modules, if I wanted a different global object depending on the value of something like "is in production", I would write
// app.ts
export {}
(async function run() {
const singletonConditionalModuleSpecifer = prodMode
? "./prod-singleton"
: "./dev-singleton";
const singleton = await import(singletonConditionalModuleSpecifer);
// use singleton
}());
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745038724a4607681.html
评论列表(0条)