Singleton¶
Intent¶
Guarantee a single instance and provide a global access point.
Use When (Rare)¶
- You truly need a single instance for correctness (e.g., a process-wide coordinator) and can’t inject it cleanly.
- The instance is stateless or effectively immutable and is safe to share across threads.
Prefer Something Else When (Usually)¶
- You can pass dependencies explicitly (constructor parameters) or use a DI container with controlled lifetimes.
- You want good testability; singletons behave like hidden global state.
Minimal Structure¶
- Private constructor
- Static/module-level access method (
getInstance()or language equivalent) - Thread-safe initialization if applicable
Implementation Steps¶
- Attempt DI first (composition root picks the instance and injects it).
- If still necessary, implement singleton with clear lifecycle and concurrency semantics.
- Avoid storing request/user-specific state on the singleton.
Pitfalls¶
- Hidden dependencies: code that “reaches out” to the singleton is hard to test and reason about.
- Global mutable state: creates flaky tests and spooky action at a distance.
- Concurrency: lazy init must be thread-safe; shared state must be protected or immutable.
Testing Checklist¶
- Ensure only one instance exists in normal execution.
- Prefer tests that avoid relying on global singleton state; if unavoidable, provide reset hooks only in test builds.