Creational Pattern Series | Singleton

Ali Mohammad
Stackademic
Published in
5 min readApr 17, 2024

--

In this series we will take a look at the creational design patterns:

  1. Singleton
  2. Factory
  3. Abstract Factory
  4. Prototype
  5. Builder

❤️ What it is?

The singleton pattern allows you to have a class that has only one instance, while providing global access to that instance.

🫡 The Why…

You might ask yourself, “Why in the world would anyone want to control how many instances a class has?” The simple answer is to limit access to shared resources like databases.

We can’t do that with regular constructors, so we have to make a static method that acts as a constructor. Behind the scenes, it just calls the private constructor to make an object and saves it in a static field.

🎨 There are three common types of singleton pattern:

  1. Eager Initialization: In this approach, the singleton instance is created at the time of class loading, before any client can request it. This ensures that the singleton instance is always available, but it can lead to unnecessary resource utilization if the singleton is not always required.
  2. Lazy Initialization: In this approach, the singleton instance is created only when it is first requested by a client. This can reduce resource utilization, but it can also introduce synchronization issues if multiple threads are simultaneously requesting the singleton.
  3. Double Checked Locking: This approach combines the advantages of the previous two methods by using lazy initialization and synchronization. The singleton instance is created only when it is first requested, but the creation process is synchronized to ensure that multiple threads do not create multiple instances. Additionally, the synchronization is performed only during the first request, so subsequent requests do not incur the overhead of synchronization.

🛞 The structure

We can implement the Singleton pattern by creating a class with a private constructor, a private static instance of the class, and a public static method that returns the instance. The private constructor ensures that the class cannot be instantiated from outside the class, and the private static instance property holds the single instance of the class. Finally, the public static getInstance() method is used to return the single instance of the class.

Eager Initialization

class DatabaseSingleton {
private static instance: DatabaseSingleton = new DatabaseSingleton();

private constructor() {}

public static getInstance(): DatabaseSingleton {
return this.instance;
}

public query(sql: String): void {
// ...
}
}

Lazy Initialization

class DatabaseSingleton {
private instance: DatabaseSingleton | null = null;
private constructor() {
// ...
}
getInstance(): DatabaseSingleton {
if (!instance) {
instance = new DatabaseSingleton();
}
return instance;
}
}

Double Checked Locking

class DatabaseSingleton {
private static instance: DatabaseSingleton | null = null;
private constructor() {}
public static getInstance(): DatabaseSingleton {
if (!this.instance) {
synchronized(DatabaseSingleton) {
if (!this.instance) {
this.instance = new DatabaseSingleton();
}
}
}
return this.instance;
}
}

Let’s take an example. Let’s say that in our application we are considering using a SQL database, and we really don’t want a million copies of the connector lying around our app.

class DatabaseSingleton {
private instance?: DatabaseSingleton = null;
private database: SQLDatabase = new SQLDatabase();
private constructor() {
// ...
database.connect();
// ...
}
getInstance(): DatabaseSingleton {
if (!instance) {
instance = new DatabaseSingleton();
}
return instance;
}
insert<T>(record: T) {
// ...
database.insert<T>(recrod);
}
}

In the above example, we define a class called DatabaseSingleton with a private constructor and a private static property called instance. The getInstance() method checks if the instance property is already set. If it's not set, it creates a new instance of the class and sets the instance property to that instance. If the instance property is already set, the method simply returns the existing instance.

Here’s an example of how to use the Singleton class:

const instance1 = DatabaseSingleton.getInstance();
const instance2 = DatabaseSingleton.getInstance();
console.log(instance1 === instance2); // true

In the above example, we create two instances of the Singleton class using the getInstance() method. Since the Singleton pattern restricts the instantiation of a class to a single instance, both instance1 and instance2 are actually references to the same object, and the console.log() statement will output true.

const databaseSingleton = DatabaseSingleton.getInstance();
databaseSingleton.insert(...);

🤓 So, to conclude:

  1. Add a private static field to the class for storing the singleton instance
  2. Declare a public static creation method for getting the single- ton instance.
  3. Implement “lazy initialization” inside the static method. It should create a new object on its first call and put it into the static field. The method should always return that instance on all subsequent calls.
  4. Make the constructor of the class private. The static method of the class will still be able to call the constructor, but not the other objects.
  5. Go over the client code and replace all direct calls to the singleton’s constructor with calls to its static creation method.

✅ Advantages

The Singleton pattern has several advantages. First, it provides a global point of access to a single instance, making it easy to coordinate actions across the system. Second, it ensures that only one instance of a class is created, which can help with memory management and performance. Finally, it can be used to enforce constraints on object creation, such as ensuring that only one instance of a class is created.

Employ the Singleton pattern when a class in your program should have only one instance available to all customers, such as a single database item shared by all components of the application.

❌ Drawbacks

However, the Singleton pattern also has some drawbacks. It can make code harder to test, since it relies on global state. It can also make it harder to reason about code, since the behavior of a Singleton class can depend on the order in which it is used. Finally, it can make it harder to extend code, since it can be difficult to modify the behavior of a Singleton class without modifying the class itself.

In conclusion, the Singleton pattern is a useful design pattern that makes sure there is only one instance of a class and gives access to that instance from everywhere.

Hope you enjoyed the article ❤️

Stackademic 🎓

Thank you for reading until the end. Before you go:

--

--