Creational patternsGuide 7 of 28
Singleton
How to guarantee a single instance, and what offering global access to it means for dependencies and tests.
Updated 7 min read
// on this page
Singleton is a creational pattern that guarantees a class has a single instance, and gives a global access point to it.
The problem
AndesShop needs an InventoryLedger: the record that keeps the current stock count for each product. If different parts of the system (checkout, the admin panel, the job that syncs with the warehouse) each create their own instance of this ledger, every instance ends up with its own copy of the count in memory — and those copies drift apart. Two different instances could each decide, on their own, that one unit of the same product is still available, and both sell it.
The problem isn’t only avoiding duplicate instances: it’s that the whole system also needs to find the same instance without every part of the code having to receive it explicitly as a parameter from the application’s entry point.
The solution
Singleton makes the class’s constructor private, so nobody can instantiate it directly with new, and exposes a static method that creates the single instance the first time it’s asked for and returns that same instance on every later call.
classDiagram
class InventoryLedger {
-instance InventoryLedger$
-stock Map~String, Integer~
-InventoryLedger()
+getInstance() InventoryLedger$
+reserve(String, int) boolean
}
class Checkout {
+confirm(Order) void
}
class WarehouseSyncJob {
+run() void
}
Checkout ..> InventoryLedger : getInstance()
WarehouseSyncJob ..> InventoryLedger : getInstance()
note for InventoryLedger "The underline ($) marks static members.
The private constructor prevents instantiating it with new."Example in Java
class InventoryLedger {
private static volatile InventoryLedger instance;
private final Map<String, Integer> stock = new ConcurrentHashMap<>();
// Private constructor: nobody can do "new InventoryLedger()" from outside
private InventoryLedger() {}
public static InventoryLedger getInstance() {
if (instance == null) {
synchronized (InventoryLedger.class) {
if (instance == null) {
instance = new InventoryLedger();
}
}
}
return instance;
}
public boolean reserve(String sku, int quantity) {
// compute() applies the check and the decrement as a single atomic
// operation on that key. That's what makes the concurrent reservation safe.
Integer remaining = stock.compute(sku, (key, available) -> {
int current = available == null ? 0 : available;
return current < quantity ? current : current - quantity;
});
return remaining != null && remaining >= 0 && stock.get(sku) != null;
}
}
// Client code, from anywhere in the system: always the same instance
boolean reserved = InventoryLedger.getInstance().reserve("JKT-2024-BLUE", 1);
When to use it
- When you need a class to have exactly one instance, reachable from anywhere in the code, typically to coordinate access to a shared resource (a cache, a registry, a connection pool).
When to avoid it
Singleton is, in practice, the most debated pattern in the catalog — because it introduces global state, which makes tests harder to write (instances stay shared across tests unless you reset them explicitly) and hides a real dependency behind a static call instead of receiving it explicitly. Before using it, it’s worth evaluating whether creating a single instance at the application’s entry point and injecting it where needed is enough, without enforcing the constraint at the class level.
Put in terms of the dependency inversion principle: InventoryLedger.getInstance() is a dependency that appears in nobody’s signature. A Checkout that calls it depends on a concrete class without declaring it, and there’s no way to substitute it in a test without touching global state. Receiving an InventoryLedger through the constructor keeps “there’s only one instance” — the entry point creates it — and removes both expensive consequences.
Benefits and drawbacks
| Benefits | Drawbacks |
|---|---|
| Guarantees a single instance and a known access point to it | Introduces global state: any module can call it without declaring it, and that couples code that should be independent |
| Controls access to a shared resource from one place | Makes testing in isolation harder, unless it’s explicitly designed to be resettable |
| Allows lazy initialization, if the implementation chooses it (it isn’t inherent to the pattern) | In multithreaded environments, a badly written implementation can create more than one instance |
Relationship with other patterns
- Abstract Factory, Builder, and Prototype can be implemented as a Singleton when a single shared factory or builder is enough.
- Facade is often implemented as a Singleton, though that isn’t a requirement of the pattern.