One of the simplest and easiest to understand among design patterns is the Singleton Design Pattern. It is a creational pattern which centralizes the creation of an instance of a class. This also helps to ensures that the class has only one instance across application. Singleton provides a global access to an instance of a class.
Problem statement
Imagine we want to ensure that only one instance of a class is created within an application. This class can be a application host class like App in a WPF / Silverlight application or some of the operating system services like the logon service or the service which listens to the mouse or keyboard events. We need to ensure that at any point of time there is only one instance of this services.
Classic Singleton
In order to ensure that there is only one instance of a class across an application boundary, we can restrict the mechanism through which new instances of a class are created. In C#, since we use constructors to create new instances, we can restrict the constructor itself. In general the constructors are mostly publicly accessible. But in case of Singleton implementation we make the constructor as private. This means that we cannot create a new instance of a class using the constructor from any other class. By making the constructor private, we force the class to take the responsibility of creating its own instance. Here is a classic implementation of the Singleton pattern as described in the Gang of Four book.
public class SimpleSingleton
{
private static SimpleSingleton _instance;
private SimpleSingleton()
{
}
public static SimpleSingleton Instance
{
get
{
if (_instance == null)
{
_instance = new SimpleSingleton();
}
return _instance;
}
}
}
In order to create an instance we create a static property or a method. In this case I have used a property. Note that this is a ready only property. In order to control the number of instances, we have declared a private static member variable. As a convention this private member as well as the public property are both named as _instance and Instance respectively. We can name it anything we would like. The getter check if the _instance is null and creates a new one if that is the case. Otherwise it simply returns the already created instance. This way we have ensured that there is only one instance of this class and also it can be created only using the same class.
This solution works fine in a single threaded model. In todays world where most of the CPU’s are multi core and most applications support multi threading in native ways, this code is bound to create problems. If multiple threads are accessing the Singleton and trying to create an instance at the same time this code is bound to fail in the step where we are trying to check if the _instance is null. One thread might find that the _instance is null and call the private constructor. Before the object is initialized, another thread might try to execute the same code and since the _instance is uninitialized another new instance might be created. This scenario clearly defeats the purpose of creating a Singleton instance.
Thread safe Singleton
To overcome this problem, we can use the synchronization mechanism and control the access to the constructor as shown below.
public class ThreadSafeSingleton
{
private static ThreadSafeSingleton _instance;
private static object _synchronizingObject = new object();
private ThreadSafeSingleton()
{
}
public static ThreadSafeSingleton Instance
{
get
{
if (_instance == null)
{
lock (_synchronizingObject)
{
if (_instance == null)
{
_instance = new ThreadSafeSingleton();
}
}
}
return _instance;
}
}
}
This approach makes the Singleton implementation Thread safe. We acquire a lock before creating a new instance. And to be double sure that the instance is not yet initialized, we check for the null value. This double checking prevents two threads from simultaneously accessing the private constructor.
Optimized Singleton
All this seems bit too much just to control instancing of an object. Dotnet framework provides an optimized way of implementing a singleton using readonly variables.
public sealed class OptimizedSingleton
{
private static readonly OptimizedSingleton _instance = new OptimizedSingleton();
private OptimizedSingleton()
{
}
public static OptimizedSingleton Instance
{
get
{
return _instance;
}
}
}
By marking the private variable as readonly, we can take care of the thread safety. The readonly variables can only be assigned during static initialization like I have done here or through a constructor. This approach is the simplest among all.
Conclusion
We saw 3 ways of implementing singleton pattern in this post. Since all the 3 approaches are already available here as inline code, this time for a change I have not uploaded the complete working solution. There is only one class involved in singleton apart from the client who uses the singleton instance. In most modern day applications we are using some for of Inversion of Control (IoC) container. These containers have built in support for converting a normal class into a singleton without writing any additional lines of code. You can use this approach demonstrated here to implement singleton pattern if you are one of those persons who do not use any IoC container for object lifecycle management. I would recommend using any of the commonly available IoC container like Spring.Net, StructureMap, Castle Windsor, NInject etc to manage the lifetime of objects as well as to resolve the dependencies among objects. It can save a lot of boilerplate code like the one shown here.
Until next time Happy Programming
Further reading
Here are some books I recommend related to the topics discussed in this post.
In addition to saving people from singleton boilerplate, IoC containers also save them from the dependency management nightmares that almost always accompany the use of singletons and static state.
ReplyDelete>> This approach is the simplest among all.
ReplyDeleteYes, because it does not use lazy initialization in contrast to other examples. And I think everyone who does not need lazy initialization should use the last approach, cause it is the safest.
Great explanation!!
ReplyDelete