These pattern pairs share surface-level similarities but solve fundamentally different problems. Interviewers love asking “What’s the difference between X and Y?” — and a vague answer signals shallow understanding.
Strategy vs. State
Both change an object’s behaviour at runtime. The difference is who triggers the change.
Strategy
State
Intent
Swap an algorithm from the outside
Object transitions its own behaviour from the inside
Who decides?
The client picks the strategy and injects it
The current state decides the next state
Awareness
Strategies don’t know about each other
States know about and transition to other states
Classic example
Payment method (CreditCard, PayPal, Crypto)
Order lifecycle (Pending → Paid → Shipped → Delivered)
Key test
Can the client swap the behaviour arbitrarily at any time?
Does the object follow a defined state machine with valid transitions?
Decorator vs. Proxy
Both wrap another object with the same interface. The difference is purpose.
Decorator
Proxy
Intent
Add new behaviour / responsibilities
Control access to the real object
Lifecycle
Client creates the real object and wraps it
Proxy often creates or manages the real object itself
Stacking
Multiple decorators stack: new A(new B(new C(real)))
Typically a single proxy wrapping the real subject
Spring AOP @Transactional proxy — controls when the real method runs
Key test
Does it add features the real object doesn’t have?
Does it control when/whether the real object is accessed?
Factory Method vs. Abstract Factory
Both decouple object creation. The difference is scope: one product vs. a family.
Factory Method
Abstract Factory
Creates
One product type
A family of related products
Mechanism
Abstract method overridden in a subclass
Interface with multiple factory methods
Extensibility
One new subclass per new product
One new factory implementation per new family
Classic example
Collection.iterator() — each collection returns its own iterator
UI toolkit: WindowsFactory creates WindowsButton + WindowsCheckbox together
Key test
Does the subclass decide which single product to create?
Do multiple products need to be used together consistently?
Composite vs. Decorator
Both use recursive composition with a shared interface. The difference is structure vs. behaviour.
Composite
Decorator
Intent
Represent tree structures uniformly (leaf = branch)
Add behaviour dynamically via wrapping
Children
Composite holds multiple children
Decorator wraps exactly one object
Delegation
Aggregates results from children (e.g. sum all sizes)
Delegates to the wrapped object, adding before/after behaviour
Classic example
File system: Directory.getSize() sums children
Coffee: new Milk(new Sugar(new Espresso()))
Key test
Do you need to treat a group the same as an individual?
Do you need to add optional features without subclassing?
Command vs. Strategy
Both encapsulate behaviour as an object. The difference is history and lifecycle.
Command
Strategy
Intent
Encapsulate a request as an object (with undo, queue, log)
Encapsulate an algorithm that can be swapped
Lifecycle
Commands are stored, queued, undone, replayed
Strategy is selected once and used — no history
Undo
Core feature: execute() + undo()
Not applicable
Classic example
Text editor: push BoldCommand to undo stack
Sort with Comparator: swap sort algorithm at runtime
Key test
Do you need to store, queue, or undo operations?
Do you need to swap one algorithm for another at runtime?
02
Composition
Compose-Together Groups
Patterns rarely work alone in real systems. These groups are natural companions — they solve adjacent problems and reinforce each other. Learning the combinations is what separates textbook knowledge from production design skill.
Composite + Iterator + Visitor
Composite defines the tree structure (files & directories)
Iterator traverses the tree without exposing its internals
Interview tip: When asked “Design a [complex system]”, identify which pattern handles each concern separately, then explain how they compose. This shows architectural thinking, not just pattern memorization.
03
Modern Alternatives
Modern Alternatives
The GoF patterns were published in 1994. Modern languages (Java 8+, Kotlin, Python) and frameworks (Spring, CDI) provide features that replace or simplify several classical patterns. Knowing the modern equivalent shows you’re not blindly applying textbook solutions.
Classical Pattern
Modern Alternative
Why the Alternative is Preferred
Singleton
Dependency Injection (@Singleton scope)
DI container manages the single instance; class itself has no static state, remains testable and mockable
Template Method
Strategy with lambdas / functional interfaces
Avoids rigid inheritance; varying steps passed as Function<T,R> or method references
Command
Runnable / Callable / lambdas
For simple fire-and-forget commands, a lambda is lighter than a full Command class. Reserve the pattern for undo/history.
Strategy
Lambdas / Comparator.comparing()
When the strategy is a single method, pass a lambda instead of creating a class: list.sort(Comparator.comparing(Order::getDate))
Observer
Reactive Streams / Flow API / Event Bus
Built-in backpressure, error handling, and composition. java.util.Observable deprecated since Java 9.
Iterator
Stream API / Iterable.forEach()
Declarative pipeline with filter(), map(), reduce() rather than imperative hasNext()/next()
Factory Method
Supplier<T> / method references
When the factory is a single no-arg constructor, Supplier<Notification> factory = SmsNotification::new replaces a subclass hierarchy
When to still use the classical pattern:
Command → when you need undo, history, queuing, or macro recording — a lambda can’t undo itself
Strategy → when the strategy has multiple methods or internal state — a lambda covers only one method
Observer → when you need custom subscription logic, filtering, or priority ordering
Singleton → in libraries without a DI container, or for JVM-level resources (Runtime, SecurityManager)
Modern Java — Strategy as a lambda
// Classical: create a DiscountStrategy class// Modern: pass a Function directlyFunction<Order, BigDecimal> tenPercentOff =
order -> order.getTotal().multiply(newBigDecimal("0.90"));
Function<Order, BigDecimal> freeShipping =
order -> order.getTotal().subtract(order.getShippingCost());
// Swap strategy at runtime โ no class neededBigDecimal finalPrice = discountStrategy.apply(order);
Modern Java — Factory Method as Supplier
// Classical: abstract class with createNotification() overridden in subclasses// Modern: pass the constructor as a SupplierMap<String, Supplier<Notification>> factories = Map.of(
"email", EmailNotification::new,
"sms", SmsNotification::new,
"push", PushNotification::new
);
Notification n = factories.get("sms").get();
n.send("Your order shipped!");
04
Visual Map
Pattern Relationship Map
This map shows all major relationships between the 23 GoF patterns. Solid lines = compose together. Dashed lines = commonly confused. Blue labels = the distinguishing factor.
GoF Pattern Relationships โ Confused, Composed, and Alternatives
05
Quick Reference
Decision Cheat Sheet
When you’re staring at a confused pair, ask these one-liner questions to pick the right pattern:
If you’re wondering…
Ask this question
Answer → Pattern
Strategy or State?
Does the object change its own behaviour based on internal conditions?
Yes → State | No → Strategy
Decorator or Proxy?
Are you adding new features or controlling access to existing ones?
Adding → Decorator | Controlling → Proxy
Factory Method or Abstract Factory?
Do you need one product or a family that must match?
One → FM | Family → AF
Command or Strategy?
Do you need to store, queue, or undo the operation?
Yes → Command | No → Strategy
Composite or Decorator?
Are you building a tree or wrapping behaviour?
Tree → Composite | Wrapper → Decorator
Adapter or Bridge?
Is it designed upfront or retrofitted?
Upfront → Bridge | Retrofit → Adapter
Facade or Mediator?
Is communication one-directional (client → subsystem) or bidirectional?
One-way → Facade | Two-way → Mediator
Interview tip: When an interviewer asks “Isn’t that just a Strategy?” or “How is this different from Decorator?”, they want you to state the one distinguishing question from the table above. A crisp one-sentence answer beats a rambling explanation.