DEV Community

Artem
Artem

Posted on

Singleton Design Pattern, TypeScript example

What is Singleton?

Context: Singleton is one of the Creational design patterns
Creational design patterns

The Singleton Pattern is a design pattern used in programming to ensure that a class has only one instance and provides a global point of access to that instance. This is useful when exactly one object is needed to coordinate actions across a system.
Comparing singleton pattern and conventional implementation

The pattern typically involves:

  • Private Constructor: Prevents direct instantiation of the class from outside.
  • Static Instance: Holds the single instance of the class.
  • Static Method: Provides a way to access the instance, creating it if it doesn't already exist.

This ensures that no matter how many times you request the instance, you always get the same object.

Good Use Cases for the Singleton Pattern

1. Configuration Settings:

Applications often need a single, centralized place to manage configuration settings. A singleton ensures that all parts of the application access the same configuration.

2. Logging:

A logging system should be centralized so that all parts of an application write to the same log. Using a singleton ensures that there is only one instance of the logger.

3. Database Connection:

Managing a single database connection instance ensures efficient use of resources and avoids the overhead of opening and closing connections repeatedly.

4. Caching:

A cache should be globally accessible and consistent across the application. Using a singleton ensures there is a single cache instance.

Example of Singleton Pattern in TypeScript

Here is the code to implement a simple singleton:

class Singleton {

    private static instance: Singleton;

    private constructor() { }

    public static getInstance(): Singleton {
        if (!Singleton.instance) {
            Singleton.instance = new Singleton();
        }

        return Singleton.instance;
    }

    public doAction() {
        console.log("action");
    }

}
Enter fullscreen mode Exit fullscreen mode

Here is how to use this class:

const singleton1 = Singleton.getInstance();
const singleton2 = Singleton.getInstance();

// Outputs: true
console.log(singleton1 === singleton2);

// Outputs: 'action'
singleton1.doAction();
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • Static getInstance Method: This static method is the key to controlling the access to the Singleton instance. If the instance doesn't already exist, it creates one. If it does exist, it returns the existing instance.

  • Private Constructor: The constructor is designed to throw an error if it is called directly after the instance is created. This ensures that the Singleton instance is created only through the getInstance method.

  • Instance Variable: The static property Singleton.instance is used to store the singleton instance. This variable is checked and modified only within the getInstance method.

  • Usage: When Singleton.getInstance() is called, it either creates a new instance or returns the existing one. Attempting to directly instantiate the class using new Singleton() will throw an error, enforcing the Singleton pattern.

Criticism, or what are drawbacks of singleton pattern?

1. Global State. They are generally used as a global instance, why is that so bad? Because you hide the dependencies of your application in your code, instead of exposing them through the interfaces. It's making the system less transparent.

2. They violate the single responsibility principle: by virtue of the fact that they control their own creation and lifecycle.

3. Can lead to tighter coupling. Singletons hide dependencies within the class itself, making the system less transparent. This can lead to tighter coupling between classes and reduce modularity. They inherently cause code to be tightly coupled. This makes faking them out under test rather difficult in many cases.

4. Hard testing. Singletons can make unit testing more difficult. Since they control their instantiation, it can be hard to substitute them with mock objects or to reset their state between tests, leading to inter-test dependencies.

5. In multi-threaded applications, singletons can cause concurrency issues if not implemented correctly, as multiple threads might access and modify the singleton instance simultaneously.

6. Inflexibility: Once a singleton is implemented, changing its behavior or replacing it with a different implementation can be difficult. This inflexibility can hinder future development and adaptation.

Conclusion:

While the Singleton pattern can be useful in specific scenarios where a single instance is truly necessary, it should be used cautiously. Alternatives such as dependency injection can often provide more flexible and testable designs. When considering the Singleton pattern, weigh the pros and cons carefully to determine if it is the best fit for your specific use case.

And here is a meme that perfectly explains my evolution while gather this article :D

Meme: singleton is bad or good


Links

Top comments (0)