Singleton Design Pattern ensures that a class has exactly one copy and provide a global access point to it

Real-life example:

  • Logger
  • One instance is preferred to avoid unintended consequences.

The class must be responsible for ensuring only one instance of itself exists.

public class Logger
{
    private static Logger? _instance; 

    /// <summary>
    /// Instance
    /// </summary>
    public static Logger Instance
    {
        get
        {
            if (_instance == null) //This is not thread safe. 
            {
                _instance = new Logger();
            }

            return _instance;
        }
    }

    protected Logger()
    {
    }

    /// <summary>
    /// Singleton Operation
    /// </summary>
    /// <param name="message"></param>
    public void Log(string message)
    {
        Console.WriteLine(message);
    }
}

Above code is not thread safe. If the instance is created by another thread, it will create another instance.
To resolve thread safe issue, Lazy<T> can be used as below.

public class LazyLogger
{
    //Lazy<T>
    private static readonly Lazy<LazyLogger> LazyLoggerInstance = 
                new Lazy<LazyLogger>(() => new LazyLogger());

    /// <summary>
    /// Instance
    /// </summary>
    public static LazyLogger Instance => LazyLoggerInstance.Value;

    protected LazyLogger()
    {
    }

    /// <summary>
    /// Singleton Operation
    /// </summary>
    /// <param name="message"></param>
    public void Log(string message)
    {
        Console.WriteLine(message);
    }
}

When to Use Singleton Pattern

When there must be exactly one instance of a class, and it must be accessible to clients from a well known access point
When the sole instance should be extensible by subclassing, and client should be able to use an extended instance without modifying their code.

More real world example

Managing database connection pools
Caching frequently accessed data
Managing application configuration settings
General resource management

Outcome of Singleton Pattern

Strict control over how and when client access it
Avoid polluting the namespace with global variables
Subclassing allows configuring the application with an instance of the class you need at runtime
Multiple instances can be allowed without having to alter the client.
Violates the single responsibility principle
The object manages how it’s created and also manages own lifecycle.
In modern language, can be handled by IoC container.

Leave a Reply