Singleton Design Pattern:
The Singleton pattern ensures that a class has only one instance and provides a global point of access to it.
Standard Singleton implementation
To ensure the class has only one instance, we mark the constructor as private.
So, we can only instantiate this class from within the class.
We create a static variable that will hold the instance of the class.
Then, we create a static method that provides the instance of the singleton class.
This method checks if an instance of the singleton class is available.
It creates an instance, if its not available; Otherwise, it returns the available instance.
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton GetInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton GetInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
Issues with the standard implementation
The standard singleton implementation code works fine in Single threaded environment.
But in multithreaded environment, the GetInstance() code breaks :
public static Singleton GetInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
public static Singleton GetInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
If two threads enter the if condition at the same time, then two instances of Singleton will be created.
Handling Multithreading issue with standard singleton implementation
We can synchronize the method. So that only one thread can access it at a time.
In the below implementation, the thread locks out the shared object,
and then checks whether or not the instance has been created before creating the instance.
public sealed class Singleton
{
private static Singleton instance = null;
private static readonly object Instancelock = new object();
private Singleton() {}
public static Singleton GetInstance
{
get
{
lock (Instancelock)
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
}
}
public sealed class Singleton
{
private static Singleton instance = null;
private static readonly object Instancelock = new object();
private Singleton() {}
public static Singleton GetInstance
{
get
{
lock (Instancelock)
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
}
}
This solves the multithreading issue. But it is slow since only one thread can access GetInstance() method
at a time.
We can use following approaches for this :
1: Double checked locking
In this approach, we first check if the instance is created. If not, we synchronize.
public sealed class Singleton
{
private static Singleton instance = null;
private static readonly object Instancelock = new object();
private Singleton() {}
public static Singleton GetInstance
{
get
{
if (instance == null)
{
lock (Instancelock)
{
if (instance == null)
{
instance = new Singleton();
}
}
}
return instance;
}
}
public sealed class Singleton
{
private static Singleton instance = null;
private static readonly object Instancelock = new object();
private Singleton() {}
public static Singleton GetInstance
{
get
{
if (instance == null)
{
lock (Instancelock)
{
if (instance == null)
{
instance = new Singleton();
}
}
}
return instance;
}
}
2: Early instance creation
We can choose to create the instance of Singleton class when the class is loaded.This is thread-safe without using locking.
public class EarlySingleton {
//create instance eagerly
private static EarlySingleton instance = new EarlySingleton();
private EarlySingleton() {}
public static EarlySingleton GetInstance(){
return instance;//just return the instance
}
}
public class EarlySingleton {
//create instance eagerly
private static EarlySingleton instance = new EarlySingleton();
private EarlySingleton() {}
public static EarlySingleton GetInstance(){
return instance;//just return the instance
}
}
3: Fully Lazy Instantiation
Here, instantiation is triggered by the first reference to the static member of the GetInstance class,
which only occurs in Instance.
public sealed class Singleton
{
private Singleton()
{
}
public static Singleton GetInstance { get { return GetInstance.instance; } }
private class GetInstance
{
// Explicit static constructor to tellcompiler
// not to mark type as beforefieldinit
static GetInstance()
{
}
internal static readonly Singleton instance = new Singleton();
}
public sealed class Singleton
{
private Singleton()
{
}
public static Singleton GetInstance { get { return GetInstance.instance; } }
private class GetInstance
{
// Explicit static constructor to tellcompiler
// not to mark type as beforefieldinit
static GetInstance()
{
}
internal static readonly Singleton instance = new Singleton();
}
4: Singleton Implementation using Generics
This will give you a single instance and will effectively be lazy,
because the static constructor doesn’t get called until Build() is called.
public class GenericSingleton<T> where T : new()
{
private static T ms_StaticInstance = new T();
public T GetInstance()
{
return ms_StaticInstance;
}
}
GenericSingleton<SimpleType> instance = new GenericSingleton<SimpleType>();
SimpleType simple = instance.GetInstance();
public class GenericSingleton<T> where T : new()
{
private static T ms_StaticInstance = new T();
public T GetInstance()
{
return ms_StaticInstance;
}
}
GenericSingleton<SimpleType> instance = new GenericSingleton<SimpleType>();
SimpleType simple = instance.GetInstance();
5: using Lazy<T> type
You need to pass a delegate to the constructor which calls the Singleton constructor –
which is done most easily with a lambda expression.
public sealed class Singleton
{
private static readonly Lazy<Singleton> lazy =
new Lazy<Singleton>(() => new Singleton());
public static Singleton GetInstance { get { return lazy.Value; } }
private Singleton()
{
}
}
public sealed class Singleton
{
private static readonly Lazy<Singleton> lazy =
new Lazy<Singleton>(() => new Singleton());
public static Singleton GetInstance { get { return lazy.Value; } }
private Singleton()
{
}
}
Even though we avoided multiple instance creation of singleton class
by using Double checked locking or Eager instance creation, instances can still be created by :
– cloning
– reflection
– Sub-classing singleton class
How to avoid Singleton instance creation by cloning ?
We can create a copy of an object using clone() method.
To avoid creating a clone of our singleton class, we can do the following :
– Implement MethodwiseClone()
– Override the clone() method and throw CloneNotSupportedException from it.
protected object MemberwiseClone()
{
throw new Exception("Cloning a singleton object is not allowed");
}
How to avoid Singleton class instance creation through Reflection ?
To avoid instance creation through reflection, throw an exception from constructor if it already has an instance.
private Singleton()
{
if (instance != null)
{
throw new Exception("Cannot create singleton instance through reflection");
}
}
How to avoid a subclass creating singleton instance ?
If you just provide a private constructor, a subclass cannot instantiate it to create another instance.
Uses of Singleton Design Pattern :
Logger :
Singleton pattern is a good option for the Logger class when we want to create
one log file with the logged messages.
If we have more than one instances of Logger in the application,
a new log will be created with every instance.
Cache :
We can use Singleton pattern to implement caches as that provides a single global access to it.
Why singleton pattern is considered an Anti-pattern ?
– Singletons aren’t easy to handle with unit tests. You can’t control their instantiation and they may retain state across invocations.
– Memory allocated to an Singleton can’t be freed.
– In multithreaded environment, access to the singleton object may have to be guarded (e.g. via synchronization).
– Singletons promote tight coupling between classes, so it is hard to test
Difference between Static class and Singleton Pattern:
– In c# a static class cannot implement an interface. When a single instance class needs to implement an interface for some business reason or IoC purposes, you can use the Singleton pattern without a static class.
– You can clone the object of Singleton but, you can not clone the static class object
– Singleton object stores in Heap but, static object stores in stack
– A singleton can be initialized lazily or asynchronously while a static class is generally initialized when it is first loaded
You can find more blogs on our website: Trigya Technologies
No comments:
Post a Comment