Design Patterns That Actually Matter (With Java Examples)
Not the 23 Gang of Four patterns recited from memory: the five we actually use in production, Java examples, when applying a pattern is the wrong call, and why interviews still ask about them.
Michele Cimmino · CEO & Academy Director, Lasting Dynamics · August 25, 2026 · 6 min read
Design patterns have a reputation problem, and they earned it: generations of developers learned them as a catechism — 23 Gang of Four names to recite in interviews — then applied them at random, producing AbstractSingletonProxyFactoryBean and other creatures nobody asked for. At Lasting Dynamics we use patterns every day and teach them in our academy, but we teach them as what they are: shared vocabulary for recurring problems, not points to collect.
This is the practical version: the patterns we actually reach for, Java examples, and — just as important — when reaching for one is the mistake.
What design patterns actually are
A design pattern is a named solution to a recurring design problem. The value isn't the solution (usually obvious once seen) — it's the shared name. When someone says "I'd make this a Strategy" in code review, the team transfers an entire design in three words. Patterns are communication compression before they are technique.
The honest corollary: a pattern applied where the problem doesn't exist isn't architecture — it's cost. The right question is never "which pattern can I use here?" but "what problem do I have, and does it already have a name?"
The five we actually use
1. Strategy — when the algorithm must be swappable
The most useful pattern there is, and the simplest: interchangeable behaviors behind one interface.
interface PricingStrategy {
BigDecimal price(Order order);
}
class StandardPricing implements PricingStrategy { /* ... */ }
class BlackFridayPricing implements PricingStrategy { /* ... */ }
class Checkout {
private final PricingStrategy pricing;
Checkout(PricingStrategy pricing) { this.pricing = pricing; }
}
Every if (type == X) ... else if (type == Y) chain that grows each sprint is a Strategy asking to be born. In modern Java a lambda or a Function<Order, BigDecimal> often suffices — the pattern is the idea, not the class hierarchy.
2. Factory Method — when creation is a decision
If constructing an object involves logic (which implementation? which dependencies?), that logic deserves exactly one home:
class NotifierFactory {
Notifier forChannel(Channel c) {
return switch (c) {
case EMAIL -> new EmailNotifier(smtp);
case SMS -> new SmsNotifier(twilio);
case PUSH -> new PushNotifier(fcm);
};
}
}
The real benefit: client code depends on Notifier, never on the concretes. It's the working sibling of the dependency inversion we cover in our clean architecture guide.
3. Observer — when something happens and others must know
Domain events: an order is confirmed, and inventory, email and analytics must react — without Order knowing any of them. In Java you'll meet it as listeners, Spring events or a message broker more often than as a hand-rolled implementation, but the design is the same: the emitter doesn't know the listeners. It's the pattern that keeps coupling low in systems that grow.
4. Adapter — when the outside world doesn't speak your language
Every serious integration has one: the payment library has its API, your domain has its interface, the adapter translates. The strategic value is the boundary: when the provider changes its API (it will), you rewrite the adapter, not the domain.
5. Decorator — when you add responsibility without touching the class
Logging, caching, retries around an existing service:
class CachedCatalog implements Catalog {
private final Catalog inner;
private final Map<String, Product> cache = new ConcurrentHashMap<>();
public Product byId(String id) {
return cache.computeIfAbsent(id, inner::byId);
}
}
Same interface, enriched behavior, freely composable. (It's also the design behind half the Java ecosystem, from I/O streams to middleware.)
The one to distrust: Singleton
Worth covering because interviews still ask: Singleton guarantees a single global instance. In practice, in 2026, it's almost always a warning sign — global state in disguise, hard-to-test code, hidden dependencies. If you need one instance, your dependency injection container provides it (Spring does by default, with a managed singleton scope — a very different thing). Being able to explain this in an interview is worth more than being able to implement it.
When a pattern is the wrong call
The rule we give in the academy: problem first, pattern second — never the reverse.
- If the code is simpler without the pattern, the pattern is wrong. Three
PricingStrategyimplementations, two of them empty, are anifin costume. - Patterns are extracted, not anticipated: the right moment for Strategy is the second real variant, not the first imagined one.
- If you can't name the future change the pattern protects against, you're doing ceremony.
A note for the AI-agent era: tools like Claude Code generate patterns enthusiastically — ask for a factory and you'll get a beautiful one, needed or not. The judgement about whether it's needed stays yours. Same principle as in our vibe coding guide: the AI fills in the structure; deciding the structure is the job.
Why interviews still ask (and how to answer)
Because patterns are a fast proxy for design vocabulary. The answer that lands isn't the list of 23 — it's "I'd use a Strategy here because this branch grows every sprint, and I wouldn't use a Singleton because…". The because is the signal; the name is just the label.
That's exactly how we train them in the Lasting Dynamics Academy: design patterns sit in the curriculum next to architecture, RDBMS and testing, applied on real tasks and challenged in the weekly mentor review — where "which pattern did you use and why" is a real question, every week. Free, fully remote, selective, with a job offer for everyone who completes. If you want your design vocabulary to become judgement, applications are open.
FAQ
Do I need to learn all 23? No. Learn the five above well, plus the couple your stack actually uses (in Java: Builder and Template Method come up often). Recognize the rest when you see them; nobody uses them all.
Are design patterns still relevant with modern languages?
Yes, but many dissolved into the language: lambdas instead of formal Strategies, Optional instead of Null Object, records for value objects. The problems the patterns solved still exist; the solutions just got lighter. Recognizing the problem remains the skill.
Are design patterns and architecture the same thing? Different scales of the same instinct: patterns organize classes and objects; architecture organizes modules and boundaries. A system can have perfect patterns and terrible architecture — and vice versa. You need both, which is why the academy teaches them together.