895632145562 [email protected]

Problem

At times we need to create a single instance (object) of a class in our software application with easy access via a global access point. Singleton pattern is used to achieve this. The singleton pattern is one of the best-known and widely used patterns in software engineering where a class which only allows a single instance of itself to be created, and usually gives simple access to that instance.

Intent

  • Ensure that only one instance of a class is created.
  • Provide a global point of access to the object.

UML diagram

Here is the UML class diagram of this pattern.

Implementation

There are different types of implementation of Singleton in C# and other programming languages like Java, Python etc. We can start by looking at very simple and common version first and then we can discuss other ones that provides thread safety, lazy loading and high-performance like advantages.

Version 1: Here is very simple one which is not thread safe. There are multiple issues with this Singleton

/// <summary>
/// The 'Singleton' class
/// </summary>
class Singleton
{
    private static Singleton _instance;
    // Constructor is 'protected'
    private Singleton()
    {
    }

    public static Singleton GetInstance()
    {
        // Uses lazy initialization.
        // Note: this is NOT thread safe.
        if (_instance == null)
        {
            _instance = new Singleton();
        }
        return _instance;
    }
}

class SingletonClient
{
    static void Main(string[] args)
    {
        Singleton s1 = Singleton.GetInstance();
        Singleton s2 = Singleton.GetInstance();
        Console.WriteLine($"{s1.GetHashCode()},{s2.GetHashCode()}");
Console.WriteLine($"s1 equals s2? = {s1 == s2}");
    }
}

 

Output:
46104728, 46104728
s1 == s2 = True
It involves a static member of Singleton class, a private constructor and a static public method that returns reference to the static instance. A client object of caller class can call the Static method to access this Singleton instance. Any subsequent calls to the method will return the same reference of the singleton instance.
See the output where both instance s1 and s2 gives the same hashcodes. That means there is only one instance in memory.
Here are some key things implemented in this simple version of singleton

  • Sealed class
  • Marking singleton as sealed prevents sub-classing because any subclass can create another instance and so the pattern is violated. The factory pattern can be used if you need a single instance of a base type, but the exact type isn’t known until runtime. Not a fan of this approach as it’s overdoing the pattern.
  • A single private and parameter less constructor.
  • This prevents other classes from instantiating it (which would be a violation of the pattern).
  • A static variable which holds a reference to the single created instance, if any.
  • This is our global point of access.
  • A public static means of getting the reference to the single created instance, creating one if necessary.

Applicability and examples

Here are some situations where Singleton can be effectively used. For example, Logger classes, Configuration classes, Load balancing, Caching, IO Communication, Database connection pooling etc.

Pattern breaking situations

1. Thread Safety: The pattern can break in multi thread process
2. Serialization: The pattern can break if the class is Serializable
3. Cloning: The pattern can break if it gets cloned
4. Reflection: The pattern can break using Reflection API
5. Multiple Class loaders: The pattern can break by multiple class loaders

Use with other patterns

1. Factory pattern implemented as Singleton
2. Null Object pattern as Singleton