javascript - TypeScript pattern to pass options while creating singleton instance - Stack Overflow

I have a TypeScript singleton classclass MySingleton {private static _instance: MySingleton;private con

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.

Share Improve this question edited Mar 28, 2018 at 21:27 Aluan Haddad 31.9k10 gold badges83 silver badges95 bronze badges asked Mar 28, 2018 at 20:09 Kartik BhandariKartik Bhandari 831 silver badge4 bronze badges 1
  • 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
Add a ment  | 

1 Answer 1

Reset to default 5

In 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):

  1. It is a singleton by definition
  2. It is very simple.
  3. 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:

  1. There is no need to write a private constructor to prevent external instantiation.
  2. 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).
  3. 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 as instance.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

  1. The Object object
  2. The Array object
  3. The Function object
  4. (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条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信