Singleton Design Pattern


The Singleton pattern ensures that a class has only one instance and provides a global point of access to that instance. Let's explore the potential problems that arise if you don't use these design patterns in real-world scenarios.

1. Singleton Pattern

The Singleton pattern is a design pattern that ensures a class has only one instance and provides a global point of access to that instance. If we do multiple requests to the class, will get a single copy of an instance. The Singleton pattern ensures that a class has only one instance and provides a global point of access to that instance.

Singleton Design Pattern

Advantages of the Singleton Pattern

  1. Controlled Access - Singleton ensures that there is controlled access to the instance, preventing accidental multiple instances.
  2. Reduced Memory Overhead - Since only one instance is created, it saves memory.
  3. Consistency Across the System - Having a single instance ensures consistent behavior across different parts of the application.

When to Use the Singleton Pattern

  • Resource Management - When managing resources like file handles, database connections, or logging, where only one instance should be active.
  • Configuration Settings - When an application has global configuration settings, it’s useful to have a single instance that manages these settings.
  • Cross-Cutting Concerns - For concerns like logging or caching that are used across various parts of an application, Singleton ensures consistent behavior and state.

Problems - A logging system without Singleton

Multiple Instances - Without a Singleton, multiple instances of the logging class could be created.

This could lead to following points.
  • Inconsistent Logging - Different parts of the application might log to different files or outputs, making it difficult to track issues.
  • Resource Contention - Multiple instances could try to write to the same log file simultaneously, causing file access conflicts or corrupted log files.
  • Memory Waste - Creating multiple logger instances would consume unnecessary memory.

A logging system with Singleton

You want to ensure that there's only one instance of the logger class that writes logs to a file. Multiple instances could lead to file access conflicts or inconsistent logging. Ensures that only one instance of the Logger class exists, preventing issues related to file access or inconsistent logging.

Example

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp2
{

    public class Singleton
    {
        private static Singleton _instance;

        // Private constructor ensures that the class cannot be instantiated from outside
        private Singleton() { }

        public static Singleton Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new Singleton();
                }
                return _instance;
            }
        }

        public void DoSomething()
        {
            Console.WriteLine("Singleton instance method called.");
        }
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            // Accessing the Logger instance
            Singleton logger = Singleton.Instance;

            // Usage
            logger.DoSomething();
        }
    }
}

Example Explanation

Consider the Logger class from your previous code.

  • Private Constructor -The constructor is private, so no one can create an instance of Logger directly using new Logger().
  • Static Instance - A static field _instance holds the single instance of the Logger class.
  • Thread-Safe Access - The Instance property uses a lock mechanism to ensure that only one thread can create the instance in a multithreaded environment.
  • Global Access Point - Logger.Instance provides a global access point to the single Logger instance.

Singleton Instance Creation

Singleton logger = Singleton.Instance;
Singleton logger1 = Singleton.Instance;
  1. Here, Singleton.Instance is used to access the single, globally shared instance of the Singleton class.
  2. Both logger and logger1 are references to the same instance of the Singleton class.
  3. The Instance property typically includes logic to check if an instance already exists. If it doesn't, it creates one; if it does, it returns the existing instance.

Singleton Instance Creation

logger.DoSomething();
logger1.DoSomething();
  1. The DoSomething() method is called on both logger and logger1.
  2. Since logger and logger1 refer to the same instance, calling DoSomething() on either will affect the same instance.

Lock Mechanism

Ensures thread safety, making sure that only one instance is created, even in multithreaded scenarios.

using System;

public sealed class Singleton
{
    private static Singleton _instance = null;
    private static readonly object _lock = new object();

    // Private constructor prevents instantiation from outside
    private Singleton() 
    {
        Console.WriteLine("Singleton instance created");
    }

    // Public static method to provide access to the single instance
    public static Singleton Instance
    {
        get
        {
            lock (_lock)  // Ensures thread safety
            {
                if (_instance == null)
                {
                    _instance = new Singleton();
                }
            }
            return _instance;
        }
    }

    // Example method for the Singleton class
    public void DoSomething()
    {
        Console.WriteLine("Singleton is working!");
    }
}

class Program
{
    static void Main(string[] args)
    {
        // Try to create instances
        Singleton s1 = Singleton.Instance;
        Singleton s2 = Singleton.Instance;

        // Both instances should be the same
        if (s1 == s2)
        {
            Console.WriteLine("Both instances are the same.");
        }

        s1.DoSomething();
    }
}

Above Code Explanation

  • Private Constructor - Prevents external instantiation of the class.
  • Static Instance Field - Holds the single instance of the class.
  • Lock Mechanism - Ensures thread safety, making sure that only one instance is created, even in multithreaded scenarios.
  • Instance Property - Provides global access to the instance

Real-World Example - Singleton Pattern as a Government

The government of a country perfectly fits the Singleton pattern, as there can only be one official government. It is a global point of access that identifies the group of people in charge.

Government Class (Singleton)

  • The Government class is a singleton. It contains a private static field _instance and a static method Instance to provide access to this single instance.
  • The constructor is private to ensure that no other part of the program can create additional government instances.

Thread Safety

  • A lock object ensures that only one thread can create the government instance at a time, preventing multiple instances from being created in a multi-threaded environment.

Requests from Citizens and Agencies

  • Citizen and Agency classes simulate entities that interact with the government by calling the ManageCountry() method.
  • Both send different requests, and those requests are processed by the same government instance.

Global Access

  • The main program demonstrates that both Citizen A and Agency B are interacting with the same government instance, fulfilling the Singleton pattern.
using System;

public sealed class Government
{
    // Static variable to hold the single instance of the Government class
    private static Government _instance = null;
    
    // Lock object for thread safety
    private static readonly object _lock = new object();
    
    // Private constructor ensures no other class can instantiate it
    private Government()
    {
        Console.WriteLine("Government has been created.");
    }

    // Public static method to provide global access to the instance
    public static Government Instance
    {
        get
        {
            lock (_lock)
            {
                // Create a new instance only if it doesn't exist
                if (_instance == null)
                {
                    _instance = new Government();
                }
            }
            return _instance;
        }
    }

    // Method to simulate managing country policies
    public void ManageCountry(string request)
    {
        Console.WriteLine($"Processing request: {request}");
    }
}

// Simulating citizens and agencies sending requests to the Government
public class Citizen
{
    public void RequestService(string request)
    {
        Console.WriteLine("Citizen is sending request...");
        Government.Instance.ManageCountry(request);
    }
}

public class Agency
{
    public void RequestPolicyChange(string request)
    {
        Console.WriteLine("Agency is sending request...");
        Government.Instance.ManageCountry(request);
    }
}

// Main program to demonstrate the Singleton pattern
class Program
{
    static void Main(string[] args)
    {
        // Simulating citizens and agencies making requests
        Citizen citizenA = new Citizen();
        citizenA.RequestService("Request for better healthcare");

        Agency agencyB = new Agency();
        agencyB.RequestPolicyChange("Request for tax reform");

        // Showing that both Citizen A and Agency B are using the same Government instance
        if (Government.Instance == Government.Instance)
        {
            Console.WriteLine("Both Citizen A and Agency B are interacting with the same government instance.");
        }
    }
}

Output

Singleton Design Pattern

Application of Singleton Design Pattern

Some real-time techniques in which the Singleton Design Pattern can be used.

  1. Proxies for Services - Invoking a Service API is a complex operation in an application, as we all know. The most extended process is creating the Service client to invoke the service API. If you make the Service proxy as a Singleton, your application's performance will improve.
  2. Facades - Database connections can also be created as Singletons, which improves application performance.
  3. Logs - Performing an I/O operation on a file in an application is an expensive operation. If you create your Logger as a Singleton, the I/O operation will perform better. A public and static method to get the reference to the new entity created by instance.
  4. Data sharing - If you have any constant or configuration values, you can store them in Singleton so other application components can read them.
  5. Caching - As we all know, retrieving data from a database takes time. You can avoid DB calls by caching your application's master and configuration in memory. In such cases, the Singleton class can handle caching efficiently with thread synchronization, significantly improving application performance.

Prev Next

Top Articles

  1. What is JSON
  2. How to convert a javaScript object in JSON object
  3. Some Important JSON Examples
  4. Common JSON Interview Question