Singleton Design Pattern

The Singleton Design Pattern is a creational design pattern that ensures a class has only one instance and provides a global point of access to that instance. This pattern is useful when you want to control the creation of an object and ensure that it’s shared across different parts of an application. The Singleton pattern can be implemented in various ways, and the most common use cases involve resource management, configuration settings, or any situation where a single shared object is needed.

When to Use the Singleton Pattern?

  1. When you want to restrict the instantiation of a class to just one object.
  2. When you need a global point of access to an object that should be shared across multiple parts of the application.
  3. When object creation is expensive and you want to limit the cost by using a single, shared instance.

Key Features of the Singleton Pattern

  1. Single Instance: Ensures only one instance of the class is created.
  2. Global Access Point: Provides a way to access that instance from anywhere in the application.
  3. Thread Safety (if needed): Handles concurrent access to the instance in multi-threaded applications.

What are the Pros and Cons?

Pros

  1. Global Access: Provides a single, global instance accessible throughout the application.
  2. Resource Management: Ensures only one instance of a resource, reducing duplication.
  3. Lazy Initialization: Instance is created only when needed, optimizing resource usage.
  4. Consistency: Ensures consistent behavior and state across the application.

Cons

  1. Global State: Can lead to hidden dependencies and make code harder to manage.
  2. Testing Issues: Difficult to mock or substitute in unit tests.
  3. Concurrency Problems: Needs proper synchronization in multi-threaded environments.
  4. Tight Coupling: Increases dependency between classes, reducing flexibility.

Example: Logging System

An application needs to log messages (e.g., errors, warnings, information) throughout its runtime. Instead of creating a new logger instance every time a log is generated, we use a singleton to ensure there’s only one instance of the logger, sharing the same configuration, file output, or database connection for logs.

Class Diagram

Implementation in C#

Let’s see how we can use the Singleton pattern in practice. Consider a scenario where we need a logging service that should have a single instance throughout the application lifecycle:

				
					public class Logger
{
    // Private static readonly instance, using Lazy<T> for lazy initialization
    private static readonly Lazy<Logger> _instance = new(() => new Logger());

    // Private constructor to prevent instantiation from outside
    private Logger() 
    {
        Console.WriteLine("Logger instance created.");
    }

    // Public property to access the instance
    public static Logger Instance => _instance.Value;

    // Log method
    public void Log(string message)
    {
        Console.WriteLine($"Log: {message}");
    }
}
				
			

Here’s how you would use the Logger class:

				
					class Program 
{ 
    static void Main(string[] args) 
    { 
        Logger.Instance.Log("Application started."); 
        Logger.Instance.Log("Processing data..."); 
        Logger.Instance.Log("Application ended."); 
    } 
}
				
			

Here is the output from the application

				
					Logger instance created.
Log: Application started.
Log: Processing data...
Log: Application ended.
				
			

Summary

The Singleton Design Pattern is a simple but powerful pattern that ensures a class has only one instance and provides a global access point to that instance. It is widely used when there is a need for centralized control over shared resources, such as database connections, logging services, configuration settings, etc.

Scroll to top