The Singleton design pattern is the most popular design pattern and belongs to the category of creational design patterns. The creational patterns deal with the creation of the object and also control the way objects will be created.
The Singleton design pattern is one of the most simple design patterns available from the well‑known Gang of Four design patterns. This singleton design pattern is useful when only one instance of a class is needed to implement actions across the entire system.
Table of Contents
Singleton Design Pattern
Sometimes there is a requirement in the code that at any given time we should have only one instance of some class. To restrict the instance to just one instance the other code using this class should not be able to instantiate an instance of the class in its own code instead should get the instance from some single access point where we are able to put some checks and controls to ensure that multiple instances of the class should not be allowed to be created.
Intent
The intent of the Singleton design pattern is to ensure that the class implementing this pattern has only one instance at any given time and provide a global single access point to this instance i.e. this single instance of that class is globally accessible from any other code.
UML Diagram
UML diagram of Singleton design pattern
Implementation
Standard Implementation – Lazy Instantiation
Below is the code for the standard or classic implementation of the single design pattern for any class
public class Singleton { public static Singleton? singletonInstance; private Singleton() { } public static Singleton GetInstance() { if (singletonInstance == null) singletonInstance = new Singleton(); return singletonInstance; } }
As we can see from the above code the Singleton design pattern is solving two problems at the same time. The two problems that are being solved by the Singleton design pattern are as follows
- Ensures that a class has only a single instance at any given time.
- Provide a global single access point for the instance request i.e. the instance should be globally accessible.
Since this Singleton Design Pattern is handling 2 responsibilities in the same class it is in violation of the Single Responsibility Principle of the Solid Principle. You can read more about Solid Principles in my article Solid Principles with C# .NET Core
The default constructor of the Singleton class is private, by making the constructor private the client code has been restricted from directly creating the instance of the Singleton class. In absence of the public constructor, the only way to get the object of the Singleton class is to use the global method to request an object i.e. the static GetInstance() method in the Singleton class should be used to get the object of the class.
The GetInstance() method creates the object of the Singleton class when it is called for the first time and returns that instance. All the subsequent requests for objects to the GetInstance() method will get the same instance of the Singleton class which was already created during the first request.
This standard implementation is also known as lazy instantiation as the object of the singleton class is created when it is required i.e. when there is a request for the object to the GetInstance() method.
The main problem with the standard implementation is that it is not thread-safe. Consider a scenario where 2 different requests hit the GetInstances() method at the same time so in that case, there is a possibility that two different objects might get created by both the requests.
Early or Eager Implementation
Below is the code for the early or eager implementation of the single design pattern for any class
public class Singleton { public static Singleton singletonInstance = new Singleton(); private Singleton() { } public static Singleton GetInstance() { return singletonInstance; } }
In the above code, we have created the instance of the Singleton class at the time of declaring the static instance variable of the Singleton class, so the instance of the class is created at the time when the class is loaded.
The other part of the code i.e the private constructor to restrict object creation and single global access point i.e. GetInstance() method remains the same.
Since in early instantiation, we have created the object before any request so this is also a thread-safe implementation of the singleton design pattern but use this implementation only when you have light class and it will be used in most parts of your code.
Thread Safe Implementation
For thread-safe implementation, the threads need to be synchronised during the first time creation of the Singleton class object. Below is the code for the thread-safe implementation of the single design pattern.
public class Singleton { public static Singleton? singletonInstance; private static readonly object _lock = new object(); private Singleton() { } public static Singleton GetInstance() { if (singletonInstance == null) { lock(_lock) { if (singletonInstance == null) singletonInstance = new Singleton(); } } return singletonInstance; } }
As you can see in the above code we are using a lock to acquire the mutual-exclusion lock for the object. While a lock is held by any thread then the other threads are blocked from acquiring the lock and have to wait until the lock is released.
So if two threads try to access the method GetInstance() at the same time then one thread will acquire the lock and the other thread will have to wait till the lock is released. When a second thread acquires a lock then the new object will not be created as the previous thread has already created the object and so the instance of the class is not null.
Once the object is created then subsequent requests to GetInstance() method will not be required to acquire a lock on the object as the object is already created and so the instance of the class is not null.
So by using the lock to synchronise threads we are able to implement a thread-safe lazy instantiation for the singleton design pattern implementation. Lazy initialisation is useful primarily to improve performance by avoiding creating objects if not required and thus reducing the program’s memory requirement.
Thread Safe Lazy Implementation using Lazy<T> Type
Here is an alternate implementation of the Single design pattern using the Lazy<T> type. Below is the code for the Lazy implementation of the single design pattern for any class
public class Singleton { public static Lazy<Singleton> lazySingletonInstance = new Lazy<Singleton>(() => new Singleton()); private Singleton() { } public static Singleton GetInstance { get { return lazySingletonInstance.Value; } } }
In the above code, the Lazy<T> type has been used to create an object of the Singleton class when it is required. To the constructor of the Lazy<T> class, we have passed that delegate that instantiates the object of the singleton class. Lambda expression has been used to pass the delegate.
By default, Lazy objects are thread-safe. In multi-threaded scenarios, the first thread to access the Value property of a thread-safe Lazy object initializes it for all subsequent accesses on all threads, and all threads share the same data.
Advantages of Singleton Design Pattern
- Singleton pattern ensures that only one instance of an object is created for the entire system.
- There is a single global access point which is easy to maintain as changes are localized to a single function.
- The lazy initialisation ensures that the object gets created only when it is required.
- Concurrent access to the resource by multiple clients can be better managed by implementing the singleton design pattern.
Real-World Example of Singleton Pattern
There are many applications that make use of the Singleton design pattern for the implementation of the code. Here are some of the real-world examples of Singleton Pattern
Logger Class – In applications where there is a logger utility class that is used by various clients to write messages to a single file. In this case, the logger class can be implemented as a singleton class. By implementing it as a singleton class we ensure that it has a single global access point and that no more than one object is created for the logger class. So there won’t be a contention kind of scenario where 2 objects at the same time are attempting to write to the same log file.
Configuration Class – This is one example of the implementation of the singleton design pattern. By implementing the configuration class as a singleton we can prevent multiple clients from repeatedly accessing and reading the same single configuration file.
Cache Class – In applications, there is a need to cache the data that is not frequently modified and repeatedly accessed. The singleton pattern for cache class objects can simplify the task of writing to the same as no two clients will try to update the same memory object at the same time.
ASP.NET Core supports the dependency injection (DI) software design pattern, which is a technique for achieving Inversion of Control (IoC) between classes and their dependencies. ASP.NET Core MVC controllers request dependencies explicitly via constructors. ASP.NET Core has built-in support for dependency injection (DI). The dependency injection also supports the singleton design pattern for services to ensure that only a single object of the service is created. The Singleton services are created the first time they are requested and then every subsequent request will use the same instance.
Summary
We learned about Singleton design patterns and also saw its various implementations. A Singleton design pattern should be used when only a single object is required for the entire system. Lazy instantiation is always the preferred implementation for a singleton design pattern.
We also saw how using the Lazy<T> type we were able to implement the lazy instantiation of a singleton design pattern with the use of minimal code.
Please provide your suggestions & questions in the comments section below
References – Design Patterns
You can also read my other articles related to Design Patterns here
You can also check my other trending articles on .NET Core to learn more about developing .NET Core Applications
- Microsoft Feature Management – Feature Flags in ASP.NET Core C# – Detailed Guide
- Microservices with ASP.NET Core 3.1 – Ultimate Detailed Guide
- Entity Framework Core in ASP.NET Core 3.1 – Getting Started
- Series: ASP.NET Core Security – Ultimate Guide
- ML.NET – Machine Learning with .NET Core – Beginner’s Guide
- Real-time Web Applications with SignalR in ASP.NET Core 3.1
- Repository Pattern in ASP.NET Core with Adapter Pattern
- Creating an Async Web API with ASP.NET Core – Detailed Guide
- Build Resilient Microservices (Web API) using Polly in ASP.NET Core
One Comment