1.3 Singleton (Creational) Design Pattern

What is the Singleton Design Pattern?

At its core, the Singleton pattern involves restricting the instantiation of a class to one object. It achieves this by:

  1. Providing a static method to access the instance.
  2. Ensuring that the class has only one private constructor to prevent instantiation from outside the class.

2 When to use Singleton Method Design Pattern?

Use the Singleton method Design Pattern when:

  1. There must be exactly one instance of a class and it must be accessible to clients from a well-known access point:
    1. This requirement encapsulates the essence of the Singleton pattern. It ensures that only one instance of the class exists, providing a global point of access to it. For example, in a game application, there might be a need for a single instance of a GameManager class to manage game states and resources.
  2. When the sole instance should be extensible by subclassing and clients should be able to use an extended instance without modifying:
    1. This implies that the Singleton instance should support inheritance, allowing clients to use extended versions without altering their code. This requirement can be fulfilled by designing the Singleton class with protected or virtual methods that subclasses can override. For instance, in a GUI framework, a WindowManager Singleton class might be extended to support additional window types or behaviors.
  3. Singleton classes are used for logging, driver objects, caching, thread pool, and database connections:
    1. These are classic examples where the Singleton pattern proves its worth.
      1. Logging: A Logger Singleton class ensures that all parts of the application log messages using the same logger instance, maintaining consistency in log formatting and destination.
      2. Driver Objects: For hardware interaction, a Singleton representing the hardware driver ensures that there's only one instance managing device communication.
      3. Caching: A CacheManager Singleton provides a central point for caching frequently used data, optimizing performance by reducing redundant computations.
      4. Thread Pool: A ThreadPool Singleton manages a pool of worker threads, ensuring efficient utilization of system resources in multi-threaded applications.
      5. Database Connections: A DatabaseConnection Singleton manages database connections, ensuring that all database operations share the same connection, reducing overhead and ensuring transaction consistency.

In all these cases, the Singleton pattern ensures that there's only one instance of the class, providing a centralized point of access for clients while managing global resources efficiently. It promotes code maintainability, scalability, and flexibility, making it a valuable design pattern in various software engineering scenarios.

3 Implementation in C++

#include <iostream>

class Singleton {
private:
    // Private constructor to prevent instantiation
    Singleton() {}

    // Static instance of the class
    static Singleton* instance;

public:
    // Method to access the Singleton instance
    static Singleton* getInstance() {
        // Lazy initialization: create instance only when needed
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }

    // Example method to demonstrate functionality
    void showMessage() {
        std::cout << "Hello, I am a Singleton instance!" << std::endl;
    }
};

// Initializing static member instance
Singleton* Singleton::instance = nullptr;

int main() {
    // Accessing Singleton instance
    Singleton* singletonInstance = Singleton::getInstance();
    singletonInstance->showMessage();

    return 0;
}

Key Points:

  1. Private Constructor:
    1. The constructor of the Singleton class is private, preventing the instantiation of the class from outside.
  2. Static Instance:
    1. A static member variable instance of type Singleton* holds the sole instance of the class.
  3. getInstance() Method:
    1. The getInstance() method provides a static point of access to the Singleton instance.
    2. It follows the lazy initialization approach, creating the instance only when it's first accessed.
  4. Usage:
    1. Clients can access the Singleton instance using Singleton::getInstance().