Design Patterns
Anti-Patterns & Pattern Misuse
An anti-pattern is a recognizable, recurring "solution" that looks reasonable but reliably makes a codebase worse. Many are what happens when a legitimate design pattern is applied without the problem it was meant to solve.
- Golden Hammer: forcing every problem to fit one favorite pattern or technology regardless of fit — "when your only tool is Observer, every problem looks like a notification"
- God Object / Blob: a single class that knows or does too much, violating Solid Principles's single-responsibility principle at the extreme
- Singleton overuse: using Singleton as a substitute for proper dependency management, producing hidden global state and untestable code (Creational Patterns)
- Premature pattern application: introducing a pattern's indirection (an interface with exactly one implementation, a Factory for a type that's never actually varied) before there's a real need for the flexibility it buys
- The fix for all of these is the same discipline: introduce a pattern in response to an observed, real problem — duplicated conditional logic, an untested global, a class that keeps growing — not preemptively
| Anti-pattern | What it looks like | Root cause |
|---|---|---|
| God Object | one class with dozens of methods and responsibilities | missing single-responsibility discipline |
| Singleton abuse | global mutable state accessed via getInstance() everywhere | reaching for Singleton instead of dependency injection |
| Interface pollution | an interface with exactly one implementation, never varied | Strategy/Factory applied with no actual variability to abstract over |
| Golden Hammer | the same pattern reused for unrelated problems | familiarity with one pattern outweighing fit to the problem |
// Anti-pattern: a Factory with exactly one product, forever
interface ShapeFactory { Shape create(); }
class CircleFactory implements ShapeFactory { public Shape create() { return new Circle(); } }
// If Circle is genuinely the only shape that will ever exist, this is pure ceremony:
Shape s = new CircleFactory().create();
// vs. just:
Shape s2 = new Circle(); // equally correct, and honest about there being no variability yet