Singleton is presented as one of the Creational pattern in Gang of Four's famous Design Patterns: Elements of Reusable Object-Oriented Software. It is described as the pattern used for restricting the instances of a class to just one.
Like Service Locator, Singleton also has this issue that it results in shared state across the application. The same object is being shared across the application which might lead to threading issues. The other issue with singleton is the way the shared object is provided. It is provided as Instance static property in the same class. Its type is same as the type it is contained in. All the other code consuming this singleton would be using the Instance property to access the only available instance of the type as the constructor is private. This makes it impossible to unit test the other code specially if the singleton is sealed for inheritance and Instance property in non-overridable (non-virtual). This is because we can not create stub or mock of this type to set expectations and override them.
In this post, we are discussing different approaches to singleton and their comparison. We will be discussing the general issues with singleton. The discussion would then be followed by some brief discussion of incorporating unit testing while keeping the existing singletons in our design.
Eager Initialization
This implementation of singleton structure is commonly referred as eager initialization but the way static initializers are executed in C#, it is not so much eager. With static constructors, the intitalizers are executed just prior to the static constructor. The compiler would guarantee laziness because of static constructor by not marking it with beforefieldinit flag.
This is what C# 4 Language specification has to say about the execution of static field variable initializer. It happens before the use of a static field of the class. Excerpt from C# 4.0 Language Specification:
The static field variable initializers of a class correspond to a sequence of assignments that are executed in the textual order in which they appear in the class declaration. If a static constructor (§10.12) exists in the class, execution of the static field initializers occurs immediately prior to executing that static constructor. Otherwise, the static field initializers are executed at an implementation-dependent time prior to the first use of a static field of that class.
Since Authentication Service only has one public static field. As long as the field is not used, variable initializer wouldn't get executed and no initialization takes place. Since static intiailizer only gets to execute once so only a single instance is created for the class. All the other requests would get the same instance.
Inner Holder Instance Class
This implementation would be useful in situations when the class has other properties as well. Based on the way static members are initialized in C#, access to any of the static members would initialize all of them. We might not want it for the singleton. This would certainly not be the case generally as we don't have other public static members for a singleton quite often. I think this implementation came from other languages where static members were initialized differently so in order to make sure that the member get initialized only when we desire it, they came up with this implementation.
Here since the inner class has the static member which would be initialized for singularity, only access to Instance property would lead to initialization of the singleton instance.
Please note that for any implementation using static fields, thread safety is ensured by the runtime creating only one instance of singleton type.
Lazy Singleton Instance:
In this approach, singleton instance is not created until one is requested. Once an instance is created for the particular singleton type, the same instance would be used for all the other requests for instances for the same type.
This is the most common implementation of implementation I have seen. In this approach, all the constructors of the type has private access. The class is sealed for inheritance for additional safety. And it has an static property, named Instance, with the same type. The property getter returns the same instance of the type for all the requests. It maintains a static variable for the same type and it just checks if the variable already has an object referred, if so, it just returns it, otherwise, it creates one and returns it.
The problem with this approach is that it is not thread safe. Two threads can enter the getter simultaneously. Both threads would pass the if statement and creates two different objects. Obviously the last instantiation would be used for future requests, but the other thread would use a different instance and the class obviously is not singleton anymore if it is resulting in creating more than one instance.
Synchronization Locks for Lazily Initialized Singleton:
The example presented for eager initialization just had the problem that it is not safe for multi-threaded access. So the natural solution comes to our mind, use some sort of synchronization block. Let's change the code like this:
The above code would cause the serialized execution of the getter block. Until one thread gets out of the block, the other thread can't enter. This would definitely ensure that only one thread is able to initialize it and no more than one instance of the singleton class are created. But there is a problem, this would always cause serialized execution and the singleton would be a performance bottleneck for a multi-threaded environment. After the instance is created, we don't want it to be synchronized. Let's update the code as follows and see if this would work:
This seems to ensure that once an instance is created, there is no synchronized access allowing more than one threads to pass through the getter without waiting for each others. Inside the if statement, it has a sync block. If an instance hasn't been created yet then it would make sure that only one thread goes through the block. Pretty!!! But there is a problem what if no instance has been created yet and two or more threads use the getter. They pass through the if statement successfully. Now only one thread enters the sync block, and then the others one by one. All causing a new instance to be created. This, again, breaks the singleton instance requirement as there are more than one instances.
In order to resolve the above problem, Double Checked Locking is used. There is one lock and two checks, hence the name. Inside the synchronization block, this would make sure that no instance is created by the other threads, waiting for their turn, to enter the block by introducing another if statement. This is for the thread which are already passed the first if statement and waiting for the lock. The other threads which enter the getter section after an instance is created wouldn't even be let in. Let's look at this:
.Net 4.0 Lazy<T>
We resorted to introduce sync locks because we thought this is our problem of providing thread safe access for singleton instance. As the old saying says, Avoid locks as much as possible in your code. Instead, try to transfer the responsibility to some other library which is already thread safe and people have invested so much in order to keep it like that. This would minimize the various deadlock and other performance bottleneck situations in your code.
In the below code, we have used one of the technique presented in Parallel Programming with Microsoft .net. Here we are delegating the responsibility of thread safety to Lazy<T> construct. This would also ensure the lazy initialization of the singleton instance. There is no problem that we are instantiating it in the initialization section as this would not initialize it until Value is first accessed. After the first time, this would always return the same instance. This is also very simple to understand, hence better readability. We are not using any synchronization constructs, yet the code has thread safety, Zindabad!!!
Monostate Pattern:
The whole purpose of Singleton is singularity. Monostate pattern is another way to achieve singularity without its structural constraints. Monostate doesn't require single instance constraint. There can be as many instances as you want but all would share their state hence showing behavioral singularity behavior. For monostate, the singular nature is transparent to the user of the type and supports polymorphic derivatives. You can see some comparison here: comparison between singleton and monostate pattern.
Issues with Singleton
We can certainly find a lot of references where Singleton is discussed rather mercilessly. People claim it's an antipattern rather others have called it evil. Those references definitely have a point and we must consider the singleton usage [both when to use it and the particular approach to singleton].
The way we implement singleton has a number of issues. It leads to a rigid, fragile and immobile design by bringing high level of coupling in our code [DIP]. It's against the agreed upon Dependency Inversion Principle which states:
HIGH LEVEL MODULES SHOULD NOT DEPEND UPON LOW LEVEL MODULES. BOTH SHOULD DEPEND UPON ABSTRACTIONS.
The implementation would cause other high level modules to depend on it, which is certainly not recommended. One of the biggest issue is that we would need to make compromises with unit testability of the singleton clients as singletons cannot be mocked or stubbed.
Singletons & Reflection:
Keeping constructor private, Singleton stops the otherwise instantiation of the type. In actual, this can be hacked. We can simply use reflection to create an instance of a type with private constructor as follows:
This can be used as follows for instantiating Authentication Service:
Unit Tests & Singleton:
Let's introduce the following method to AuthenticationService.
Now introduce a ShiftManager to our CoffeeHouseApp application.
It is impossible to test Login with this code as we cannot really mock the singleton class. But we cannot leave our code like this. This would then become a LEGACY CODE which is the code without any unit tests.
The virtual method would allow us to provide alternate implementation through overriding. We just need to create partial mock of ShiftManager and provide expectation for the method. This would make it easier for us to test Login method without worrying about Singleton. We can write the test for ShiftManager as follows:
Please refer this to incorporate NUnit with Visual Studio 2010. You can see the tests passing:
Singletons & Inversion of Control [IoC]:
The problem with singleton is both for its concept and implementation. Conceptually, the issue with singleton is its shared state. It's like a dignified Global Variable. Dignified, because it came from Gang of Four. If we have no issues with the concept then we don't need to use the same implementation as in the formal texts. We need a repository which could provide the same instance over and over whenever it is requested for an instance for a particular type. So a particular type might be a singleton in one application and not in others. IoC containers have made our lives easier in dealing with singletons. We can specify which type resolution should be singleton and rest is taken care of by the container. There are many IoC containers available in the market. Unity seems to have the greatest industry penetration [Mark Seemann - Dependency Injection in .Net].
Unity provides the support using Life Time Managers. While doing the type registration, we can specify how much we want the same instance to be shared. For registering a type to be shared for the lifetime of container, we use ContainerControlledLifeTimeManager
Download
References
Implementing Singletons: http://msdn.microsoft.com/en-us/library/ff650316.aspx
Double Checked Locking: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
Judith Bishop, C# 3.0 Design Patterns [Chapter 5]: http://msdn.microsoft.com/en-us/library/orm-9780596527730-01-05.aspx
Singleton Vs Static Class : http://www.iridescence.no/post/SingletonvsStaticClass.aspx
Tuesday, July 24, 2012
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment