1. Singleton
The Singleton pattern ensures a class has exactly one instance and provides a global access point to it. It is one of the most widely recognized creational patterns, used whenever a single shared object must coordinate actions across an entire application.
Intent and Motivation
Intent: Guarantee that a class is instantiated at most once and provide a single, well-known access point for that instance.
Motivation: Some resources are inherently singular — configuration registries, connection pools, logging sinks, or hardware device drivers. Creating multiple instances would waste memory, cause inconsistent state, or break coordination. Singleton centralizes control so every part of the system references the same object.
Without Singleton, developers might pass instances through constructors or dependency injection containers. When a truly global singleton is appropriate, the pattern prevents accidental duplicate creation through a controlled factory method.
Structure (UML-like)
┌─────────────────────────┐
│ Singleton │
├─────────────────────────┤
│ - instance: Singleton │ (static, holds the sole instance)
├─────────────────────────┤
│ - Singleton() │ (private constructor)
│ + getInstance(): │
│ Singleton │ (static factory method)
│ + businessMethod() │
└─────────────────────────┘
Participants:
- Singleton — defines
getInstance()and holds the static reference; constructor is private. - Client — calls
getInstance()instead ofnew Singleton().
Java Example
public final class DatabaseConnection {
private static volatile DatabaseConnection instance;
private DatabaseConnection() {
// Expensive initialization (connection pool, etc.)
}
public static DatabaseConnection getInstance() {
if (instance == null) {
synchronized (DatabaseConnection.class) {
if (instance == null) {
instance = new DatabaseConnection();
}
}
}
return instance;
}
public void query(String sql) {
System.out.println("Executing: " + sql);
}
}
// Usage
DatabaseConnection db1 = DatabaseConnection.getInstance();
DatabaseConnection db2 = DatabaseConnection.getInstance();
System.out.println(db1 == db2); // true
The double-checked locking idiom with volatile is the standard thread-safe lazy initialization approach in Java.
JavaScript Example
const Singleton = (function () {
let instance;
function createInstance() {
return {
config: { theme: 'dark', locale: 'en-US' },
get(key) { return this.config[key]; },
set(key, value) { this.config[key] = value; }
};
}
return {
getInstance() {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
const a = Singleton.getInstance();
const b = Singleton.getInstance();
console.log(a === b); // true
a.set('theme', 'light');
console.log(b.get('theme')); // 'light'
In modern JavaScript, ES module singletons (a module that exports one shared object) often replace explicit pattern code because modules are evaluated once per runtime.
Real-World Use Cases
| Framework / System | Usage |
|---|---|
| Spring Framework | @Scope("singleton") is the default bean scope — one instance per container. |
| java.lang.Runtime | Runtime.getRuntime() exposes the single JVM runtime object. |
| Android | ActivityManager, LocationManager — system services accessed via singleton getters. |
| Redux (JavaScript) | The store is a single global state container, conceptually a singleton. |
| Log4j / SLF4J | Logger factories often return shared logger instances per class name. |
Pros and Cons
| Pros | Cons |
|---|---|
| Controlled access to a sole instance | Violates Single Responsibility Principle (class manages itself + business logic) |
| Lazy initialization saves resources | Hard to unit test (global state persists between tests) |
| Global access simplifies coordination | Hides dependencies — callers use getInstance() instead of injection |
| Thread-safe variants prevent race conditions | Can become a “god object” accumulating too many responsibilities |
| Reduces memory for expensive objects | Tight coupling to the concrete class |
When to Use vs When NOT to Use
Use when:
- Exactly one instance must exist (configuration manager, audit trail, device driver).
- The instance must be accessible from many unrelated parts of the codebase.
- Lazy initialization is important (object is expensive to create and may never be needed).
Do NOT use when:
- You can achieve the same result with dependency injection (preferred in modern apps).
- The class has no reason to be global — pass the instance explicitly.
- You need multiple instances in tests or multi-tenant environments.
- Concurrency requirements make locking overhead unacceptable (consider enum singleton or DI).
Common Mistakes
- Forgetting thread safety — two threads can create two instances without synchronization.
- Allowing cloning or serialization —
clone()orreadObject()can bypass the single instance; override and block them. - Eager initialization when lazy is needed — static field initialization runs at class load, not on first use.
- Overusing Singleton — not every shared object needs this pattern; DI containers manage lifecycles better.
- Subclassing a Singleton — subclasses each get their own static instance, breaking the “one instance” guarantee unless carefully designed.
Related Patterns
- Factory Method / Abstract Factory — can ensure only one instance of each product type is created.
- Facade — often implemented as a singleton because a single facade simplifies subsystem access.
- Monostate — alternative that shares state across instances rather than sharing the instance itself.
- Dependency Injection — modern replacement that provides controlled single-instance wiring without global accessors.
Summary
Singleton is powerful when a resource is truly singular, but it is also one of the most abused patterns. Prefer dependency injection and module-level singletons in modern applications, and reserve explicit Singleton implementations for cases where global coordination is an explicit architectural requirement.