Design Patterns interview question · Most asked
How do you implement a Singleton, and why is it often considered an anti-pattern?
Summary
A Singleton has a private constructor and a static accessor that creates the single instance on first use and returns it afterwards. It's criticised because its global access hides dependencies, shares mutable state across tests and requests, and makes swapping implementations hard.
Longer Answer
The "exactly one instance" guarantee is sometimes useful (a connection pool, a hardware interface), but the global access point is the problem: any code can reach it, tests leak state into each other, and parallel code needs thread safety (in Java: a synchronized accessor, the holder idiom, or an enum).
The usual alternative: create one instance at startup and inject it, or register it with singleton scope in a DI container (Spring's default scope, Laravel's singleton()). In Python, a module-level object is the idiomatic singleton.
Code Example
class Config:
_instance = None
@classmethod
def instance(cls):
if cls._instance is None: # created lazily, on first use
cls._instance = cls()
return cls._instance
print("same object:", Config.instance() is Config.instance())
class Config {
private static _instance: Config | null = null;
private constructor() {}
static instance(): Config {
if (Config._instance === null) Config._instance = new Config(); // created lazily, on first use
return Config._instance;
}
}
console.log("same object:", Config.instance() === Config.instance() ? "True" : "False");
public class Main {
public static void main(String[] args) {
System.out.println("same object: " + (Config.instance() == Config.instance() ? "True" : "False"));
}
}
final class Config {
private Config() {}
private static class Holder { // initialisation-on-demand holder: lazy and thread-safe
static final Config INSTANCE = new Config();
}
static Config instance() { return Holder.INSTANCE; }
}
<?php
final class Config {
private static ?Config $instance = null;
private function __construct() {}
public static function instance(): Config {
return self::$instance ??= new Config(); // created lazily, on first use
}
}
echo "same object: ", Config::instance() === Config::instance() ? "True" : "False", "\n";
Key Points
- Private constructor + static accessor.
- Global access hides dependencies and leaks state.
- Thread-safe lazy init in Java: holder idiom or enum.
- Prefer one injected instance or container singleton scope.
Deep dive: Design Patterns, Explained Simply: Singleton
Explanation
What interviewers look for
A correct implementation (thread safety in Java), the testing/global-state critique, and the injected-instance alternative.
Follow-up questions
- How would you test code that uses a singleton?
- What does Spring's singleton scope mean?
More most-asked Design Patterns questions
- What problem does the Builder pattern solve? Show an example.
- Explain the SOLID principles.
- What is a design pattern, and why are they useful?
- What is the difference between dependency inversion, dependency injection and inversion of control?
- Why is composition often preferred over inheritance?
- What is dependency injection, and what forms does it take?
- Explain the Factory Method pattern and how it differs from a simple factory.
- What is the Adapter pattern? Give an example.