Singleton
Singleton guarantees one instance with global access, suiting shared resources like loggers and configuration. Its hidden global state harms testability, so dependency injection is often preferred.
The Singleton pattern ensures a class has exactly one instance and provides a global access point to it. It is used when precisely one object must coordinate actions across a system — a configuration registry, a logger, a thread pool, or a cache. While simple, it is one of the most debated patterns because global access can undermine testability and modular design.
How It Works
The class hides its constructor (making it private or non-public) so outside code cannot instantiate it, and exposes a static accessor — often getInstance() — that creates the instance on first use and returns the same one thereafter. The instance is held in a static field.
Thread safety is the core implementation concern. Lazy initialization without synchronization can create two instances under concurrent access; common fixes include eager initialization at class load, double-checked locking with a volatile field, or the initialization-on-demand holder idiom in Java. Many languages offer idiomatic alternatives: a module-level object in Python or JavaScript, an enum singleton in Java, or a dependency-injection container that registers a type with singleton scope.
When to Use It
Use a Singleton when exactly one instance is genuinely required and a global access point is appropriate — for example, a process-wide metrics collector or a read-only configuration loaded once. It is reasonable for stateless or immutable shared services.
Be cautious when the singleton holds mutable state, because that becomes hidden global state shared across the program. In most modern applications, registering a single-instance service in a DI container is preferable: it gives the same lifetime guarantee without hard-coding global access.
Trade-offs
Singletons guarantee a single instance, provide convenient access, and can lazily defer expensive initialization. For truly global, immutable resources they are pragmatic.
The downsides are significant. They introduce global state, which hides dependencies and makes code harder to test, since tests cannot easily substitute a mock and shared state leaks between test cases. They couple callers to a concrete class and can become a concurrency bottleneck. In distributed or multi-classloader environments, "one instance" may not hold. For these reasons many teams treat the classic Singleton as an anti-pattern and prefer injected, single-scoped dependencies.
Related Patterns
Multiton generalizes Singleton to a fixed set of named instances. Object Pool manages a bounded set of reusable instances rather than exactly one. Dependency Injection is the modern alternative for controlling instance lifetime without global access. Abstract Factory implementations are frequently singletons.
Example
An application logger is a singleton: Logger.getInstance().info("started"). One logger coordinates output to a shared file, preventing interleaved writes. In a DI-based codebase the same goal is met by registering Logger as a singleton-scoped service and injecting it, which keeps the single-instance guarantee while allowing a fake logger to be injected in tests.