GoF(Gang of Four) design patterns solve typical commonly occuring problems. The most common principle — Singleton.
Problem — There is large object which is accessed by many classes. Every time creation of new instance of the this huge class consumes memory and creation time.
So previously program looked like
Assume there are 2 clients which use HugeData instance. So they independently create their own objects of HugeData
Now, if we assume the single instance of HugeData consumes 1 MB of memory and takes 500 ms to create instance, we end up consuming 2 MB of data and 1s creation time. Now, if we consider there are 10 classes which create their own HugeData instances, we consume 10 MB memory and added 5 seconds of execution time. What if we could have achieved the same with 1 MB of memory and 500ms time? That is 90% saving of memory and time.
Solution — We can make a provision that across complete application we maintain single instance of that huge class.
- We saved object creation time
- We saved memory.
How to achieve this?
For the first time request to the object of the class, we create the instance and send it to client. After that, every time when the class instance is requested, we pass the previously created instance instead of creating new.
This is the Singleton design pattern. So Singleton is creational design pattern as it controls the creation of instance. So to achieve simple singleton implementation, we have to follow some rules
- Prevent creation of new instances of class using private constructor. By default constructor takes the access type of class. FOr global visibility, out HugeData class is public. So the default constructor would also be public. Anyone can add a new instance by simple doing
We need to prevent this by declaring the constructor private.
As a result of this, other classes cannot simple create instance of HugeData class. The compiler throws error if user tries to instantiate.
2. Once, we prevented the creation of object, we need to make provision to provide instance of class if requested. So we add static method, CreateInstance.
And the client code looks like
We can also add method arguments to CreateInstance method to initialize instance of HugeData class
3. If we observe the above code snippet, we still have not achieved single instance since every time the CreateInstance method is called, we return new instance. So the class needs refactoring here. Now, instead of creating instance every time requested, we maintain global variable. It will be created first time only.
The simple singleton implementation is done!! For single threaded application, this is perfect solution!!
4. However, for multi-threaded application, we may not guarantee that the single instance is available in application. How? Assume there are two different clients on different threads. First thread enters into CreateInstance() method and checks if instance is null. Since this is the first call, instance is null and thread enters further to create instance. Just before instance is created, the other thread starts execution due to round robin. It checks instance, it finds it null (since first thread hasn’t yet created any instance), so it also tries to create new instance. This results in 2 instances of HugeData class in application thus violating Singleton purpose.
To avoid this, we need to make instance check and creation thread-safe. This is called Thread-safe Singleton pattern.
We have added ‘Lock’ just before instance is checked and created. This will ensure that at a time only one thread enters the zone avoiding creation of multiple instances.
5. One problem here, the lock object is instantiated when class is loaded. So if there are two different threads load the HugeData class at the same time, they end up creating to lock objects. In that case, there is no use of lock as they are locking two different instances of lock object. So we need to prepone the creation of lock object instance such a way that the lock object will be single object throughout the application. To do so, initialize the lock object in static constructor. Since it is static variable, we can use static constructor. Also, since static constructor is executed only once in the life of that class, we can be assured that there will be single instance of it.
Conclusion — Singleton is widely used easy creational design pattern. It solves basic problem — Need single instance of class throughout application. Having said that, one should check the design before enforcing singleton pattern. Sometimes, we need multiple instances of class each having specific responsibility. Do not use singleton there!!