Design Patterns Singleton
The Singleton pattern ensures that a class has exactly one instance throughout the entire life of an application, and provides a single point of access to that instance. No matter how many times you ask for it, you always get back the same object.
The Real-World Analogy
A country has exactly one president at a time. Citizens do not create a new president every time they need one — they always refer to the same person currently in office. The Singleton pattern works identically: your code always refers back to one shared object.
Problem Without Singleton
App starts
│
├── Module A creates ConfigManager → loads settings file ✓
├── Module B creates ConfigManager → loads settings file again ✗ (wasteful)
├── Module C creates ConfigManager → loads settings file again ✗ (wasteful)
│
Result: Three different ConfigManager objects, possibly with different values.
Changes in one object are invisible to the others.
Solution With Singleton
App starts │ ├── Module A asks for ConfigManager │ └── No instance exists yet → creates one, stores it │ ├── Module B asks for ConfigManager │ └── Instance already exists → returns the same one ✓ │ ├── Module C asks for ConfigManager │ └── Instance already exists → returns the same one ✓ │ Result: All modules share one ConfigManager. Changes are visible everywhere.
UML Diagram
┌─────────────────────────────────┐ │ Singleton │ ├─────────────────────────────────┤ │ - instance: Singleton │ ← the one and only object (static) │ - data: string │ ├─────────────────────────────────┤ │ - Singleton() │ ← private constructor (blocks "new") │ + getInstance(): Singleton │ ← the only way to get the object │ + getData(): string │ └─────────────────────────────────┘
Why the Constructor Is Private
Making the constructor private blocks any outside code from writing new Singleton(). The only door in is through getInstance(), which checks whether an instance already exists before creating one.
How getInstance() Works (Step-by-Step Flow)
Call getInstance()
│
▼
Is instance null?
/ \
YES NO
│ │
▼ ▼
Create new Return existing
instance instance
│ │
▼ │
Store it │
│ │
└───────────────┘
│
▼
Return instance
Code Example (Java-style pseudocode)
class ConfigManager {
private static ConfigManager instance = null; // starts empty
private ConfigManager() {
// load config file here
}
public static ConfigManager getInstance() {
if (instance == null) {
instance = new ConfigManager(); // created only once
}
return instance;
}
public String getSetting(String key) { ... }
}
// Usage
ConfigManager config = ConfigManager.getInstance();
config.getSetting("theme"); // "dark"
// Call it again from a different class
ConfigManager same = ConfigManager.getInstance();
// same == config → true (exact same object in memory)
Thread Safety Problem (Advanced Note)
In multi-threaded applications, two threads can both check instance == null at the same time and each create their own instance. This breaks the Singleton guarantee.
Thread 1: checks instance → null → starts creating... Thread 2: checks instance → null → starts creating... ← problem! Thread 1: finishes, stores instance A Thread 2: finishes, stores instance B (overwrites A) Result: Two instances exist. Singleton is broken.
The fix is to use double-checked locking or initialise the instance at class-load time, depending on the language.
Where Singleton Is Used in Real Projects
┌─────────────────────────────────────────────────────────┐ │ Common Singleton Use Cases │ ├──────────────────────┬──────────────────────────────────┤ │ Logger │ One log file, all messages go │ │ │ to the same place │ ├──────────────────────┼──────────────────────────────────┤ │ Configuration │ One set of app settings loaded │ │ Manager │ once, shared everywhere │ ├──────────────────────┼──────────────────────────────────┤ │ Database Connection │ One connection pool to avoid │ │ Pool │ opening too many connections │ ├──────────────────────┼──────────────────────────────────┤ │ Cache Manager │ One shared cache so all parts │ │ │ of the app see the same data │ └──────────────────────┴──────────────────────────────────┘
Advantages and Disadvantages
ADVANTAGES DISADVANTAGES ───────────────────────────────── ───────────────────────────────── Saves memory (one object only) Hard to unit-test in isolation Consistent shared state Global state can cause hidden bugs Lazy initialisation possible Violates Single Responsibility Simple to access from anywhere Can become a bottleneck under load
When to Use Singleton
- Use it when exactly one object must coordinate actions across the system.
- Use it for shared resources like loggers, config files, or thread pools.
- Avoid it for objects that naturally need multiple instances, such as user profiles or shopping carts.
Key Takeaways
- Singleton allows only one instance of a class to exist at any time.
- The private constructor prevents external code from creating new instances.
getInstance()is the single entry point that creates or returns the shared object.- Thread safety needs extra care in concurrent applications.
