The Singleton Pattern
The Singleton Pattern is probably the most widely used (and abused) design pattern, which is a shame because it has some serious flaws.
Developers tend to apply the Singleton Pattern when they want to ensure the existence of only one object instance. One object means only a single copy of the state exists at any given point in time.
Some situations where we might want to have only a single instance are for a database connection, logger, or cache. In these use cases, we often desire only a single instance of the same state. Alternatively, some objects could be very resource-intensive to create. An optimisation might be to instantiate such an expensive object only once and then reuse it.
Here is a description of how to create a Singleton:
- Declare the constructor as
private
, to avoid direct instantiation. - Declare a
private static Instance
property (or class member) to hold our single instance. - Define a
public static
method (or property) that returns the singleton instance. We can use lazy instantiation to create a new instance when it doesn’t exist yet.
A simple Singleton implementation would look like this:
public class Singleton { private static Singleton Instance = null; private Singleton() { } public static Singleton GetInstance() { if (Instance == null) Instance = new Singleton(); return Instance; } // Instance methods/properties to call on our Singleton instance. public string Data { get; set } // ... }
How does this code behave?
var singleton = Singleton.GetInstance(); var anotherReference = Singleton.GetInstance(); singleton.Data = "abc"; Console.WriteLine($"Set singleton.Data to {singleton.Data}"); Console.WriteLine($"singleton.Data: {singleton.Data}"); Console.WriteLine($"anotherReference.Data: {anotherReference.Data}"); Console.WriteLine(); anotherReference.Data = "xyz"; Console.WriteLine($"Set anotherReference.Data to {anotherReference.Data}"); Console.WriteLine($"singleton.Data: {singleton.Data}"); Console.WriteLine($"anotherReference.Data: {anotherReference.Data}");
And the output is:
Set singleton.Data to abc singleton.Data: abc anotherReference.Data: abc Set anotherReference.Data to xyz singleton.Data: xyz anotherReference.Data: xyz
As we see, irrespective of which reference we are using, we are referring to to the same instance. A change to Data
in one is reflected in the other.
The Singleton pattern has its problems:
- If we change our minds about the class being a Singleton, we will need to modify all the calls to
GetInstance()
. Potentially such a change will touch many areas of our application. - The
GetInstance()
method in our simple example is not threadsafe. A careless implementation ofGetInstance()
could have multiple concurrent threads instantiate several copies of the data. When we have simultaneous threads,GetInstance()
will require a locking mechanism. - Last but not least, Singleton code can be tricky in unit tests. The Singleton may represent an entity that we would prefer to mock rather than refer to the real object in a unit test, like a database connection. Naturally, we don’t want to call into a database in a unit test. However, the
GetInstance()
method isstatic
, and statics are notoriously difficult to mock. We may have to use advanced unit testing techniques, like Extract & Override, to overcome this obstacle. We’ll look into Extract & Override another time.
There is a better alternative to the Singleton Pattern. We’ll check this out tomorrow.
Leave a Reply
Want to join the discussion?Feel free to contribute!