Loading learning content...
Having studied all seven structural patterns—Adapter, Bridge, Composite, Decorator, Facade, Flyweight, and Proxy—you might feel equipped to tackle any structural design challenge. But here's the reality that separates junior developers from Principal Engineers: knowing patterns is not the same as knowing when to use them.
The structural patterns share a fundamental characteristic: they all involve object composition to form larger structures. This shared DNA creates subtle overlaps that confuse even experienced developers. When should you use a Decorator versus a Proxy? Is your problem better solved by a Facade or an Adapter? Could a Composite handle what you're trying to achieve with a Bridge?
These questions don't have trivial answers. The right choice depends on understanding the intent behind each pattern, the forces they address, and the consequences they produce. This page provides that understanding through rigorous, side-by-side comparison.
By the end of this page, you will understand the precise distinctions between all seven structural patterns. You'll recognize the signature problems each pattern addresses and understand why choosing the wrong pattern creates more problems than it solves. This is pattern fluency—the ability to see past surface similarities to the essential differences that matter.
Before comparing individual patterns, we must understand what unifies them. All structural patterns address a common meta-problem: how to compose classes and objects to form larger, more complex structures while maintaining flexibility and efficiency.
This meta-problem manifests in different specific challenges:
Each structural pattern provides a specialized solution optimized for one of these challenges. The key insight is that patterns are not interchangeable tools—they're precision instruments designed for specific purposes.
A pattern is correct when it matches the forces in your problem domain. Forces include constraints like performance requirements, flexibility needs, team expertise, and maintenance expectations. The 'best' pattern is the one that best resolves these forces with minimal side effects.
The Classification Framework:
To compare patterns systematically, we need a framework. Each pattern can be analyzed across multiple dimensions:
1. Primary Intent — What fundamental problem does it solve? 2. Structural Mechanism — How does it achieve its goal compositionally? 3. Runtime Behavior — What happens when the pattern is in action? 4. Flexibility Axis — What becomes easier to change? 5. Complexity Trade-off — What complexity does it introduce? 6. Typical Collaborators — What other patterns often appear alongside it?
Let's apply this framework to create a comprehensive comparison.
The most important distinction between patterns is their intent—the fundamental problem they solve. Two patterns might have similar structures but vastly different purposes. Understanding intent is the first step in pattern selection.
| Pattern | Primary Intent | Problem Signature | Key Question |
|---|---|---|---|
| Adapter | Interface compatibility | Existing class with incompatible interface | "How do I use this class that doesn't fit my interface?" |
| Bridge | Abstraction/implementation decoupling | Both abstraction and implementation need to vary independently | "How do I avoid a Cartesian product of subclasses?" |
| Composite | Part-whole uniformity | Tree structures where individual and composite are treated the same | "How do I treat single objects and groups uniformly?" |
| Decorator | Dynamic responsibility addition | Need optional, stackable enhancements to objects | "How do I add behavior without modifying the class?" |
| Facade | Subsystem simplification | Complex subsystem with many interdependent classes | "How do I provide a simple interface to this complex system?" |
| Flyweight | Memory optimization through sharing | Many similar objects with shareable intrinsic state | "How do I support many objects without running out of memory?" |
| Proxy | Controlled access and lifecycle | Need to control access, add logging, defer creation, or manage remoteness | "How do I control access or add indirection to an object?" |
Notice that Decorator and Proxy can have nearly identical class diagrams—both wrap a target object and implement the same interface. But their intentions are completely different: Decorator adds behavior, Proxy controls access. Choosing between them requires understanding intent, not structure.
While intent differentiates why you use a pattern, the structural mechanism differentiates how the pattern achieves its goal. Understanding these mechanisms helps you recognize patterns in existing code and implement them correctly.
| Pattern | Structural Mechanism | Object Relationships | Typical Multiplicity |
|---|---|---|---|
| Adapter | Wraps one interface to present another | Adapter → Adaptee (composition or inheritance) | 1:1 (one adapter per adaptee type) |
| Bridge | Separates abstraction from implementation via composition | Abstraction → Implementor (composition) | 1:1 at runtime, M:N possible classes |
| Composite | Recursive tree structure with uniform interface | Composite → Component* (recursive aggregation) | 1:N (tree structure) |
| Decorator | Stackable wrappers adding behavior | Decorator → Component (recursive composition) | 1:1 per layer, stackable N layers |
| Facade | Unified interface aggregating subsystem | Facade → Subsystem classes (aggregation) | 1:N (one facade, many subsystem classes) |
| Flyweight | Shared intrinsic state separated from context | Context → Flyweight (shared reference) | N:1 (many contexts share one flyweight) |
| Proxy | Surrogate controlling access to real subject | Proxy → RealSubject (composition) | 1:1 (one proxy per subject) |
Key Structural Insights:
Adapter vs. Bridge: Both involve wrapping, but Adapter adapts an existing interface to a target interface (reactive), while Bridge proactively separates abstraction from implementation to allow independent evolution. Bridge is designed upfront; Adapter is applied after the fact.
Decorator vs. Proxy vs. Adapter: All three wrap objects. The distinction:
Composite vs. Decorator: Both use recursive composition. Composite builds tree structures for part-whole hierarchies; Decorator chains for behavior addition. Composite is about structure, Decorator is about behavior.
Facade vs. Adapter: Both simplify usage. Facade simplifies a group of classes (subsystem); Adapter makes one class fit an expected interface.
Certain pattern pairs cause persistent confusion because their structures appear similar or their problem domains overlap. Let's disambiguate the most commonly confused pairs with precision.
Both Decorator and Proxy wrap a target object and implement the same interface. Their UML diagrams can be nearly identical. The difference is entirely in intent and lifecycle responsibility.
Adapter vs. Facade — Scope and Purpose:
| Dimension | Adapter | Facade |
|---|---|---|
| Scope | Single class interface translation | Entire subsystem simplification |
| Purpose | Make incompatible interface compatible | Provide simpler interface to complex system |
| Direction | Translates TO expected interface | Provides NEW simplified interface |
| Classes involved | Typically one adaptee | Many subsystem classes |
| When to use | Working with legacy/third-party classes | Simplifying complex internal subsystems |
Bridge vs. Strategy — Structural vs. Behavioral:
Bridge and Strategy have similar class diagrams: an abstraction delegates to an implementation through composition. The difference:
Bridge separates abstraction from implementation of the same concept. Strategy separates the algorithm from its usage context.
Every pattern introduces trade-offs. A pattern that solves one problem often creates new considerations. Understanding these trade-offs is essential for informed pattern selection.
| Pattern | Benefits | Costs | Careful When... |
|---|---|---|---|
| Adapter | Reuses existing functionality, decouples client from adaptee | Adds indirection, extra class per adaptation | Adaptee interface changes frequently |
| Bridge | Independent evolution of abstraction and implementation | Increased complexity, requires upfront design | You only have one implementation |
| Composite | Simplifies client code, easy to add new components | Can make design overly general, hard to restrict component types | You need strict type constraints |
| Decorator | Flexible alternative to subclassing, runtime composition | Many small objects, can be hard to debug/understand layers | Object identity matters (a ≠ decorated(a)) |
| Facade | Reduces coupling, simplifies usage | Doesn't prevent direct subsystem access, can become god class | Facade becomes too large/omniscient |
| Flyweight | Massive memory savings for large object counts | Complexity of separating intrinsic/extrinsic state, can reduce encapsulation | State is hard to separate cleanly |
| Proxy | Lazy init, access control, remote transparency | Indirection overhead, increased complexity | Real subject performance is critical |
Every structural pattern introduces indirection. Indirection enables flexibility but can obscure the actual runtime behavior. Before applying a pattern, ask: 'Is the flexibility I gain worth the complexity I'm adding?' If the answer is unclear, you may be over-engineering.
Visual representations help internalize the structural differences between patterns. The following diagrams highlight the essential compositional structure of each pattern family.
Pattern Families:
1. Wrapper Patterns (Adapter, Decorator, Proxy) — All involve wrapping a single object. Differentiated by purpose: interface translation, behavior addition, or access control.
2. Hierarchy Patterns (Composite, Facade) — Both manage multiple objects. Composite creates recursive structures; Facade aggregates subsystem components behind a unified interface.
3. Special Purpose Patterns (Bridge, Flyweight) — Designed for specific concerns: orthogonal variation (Bridge) and memory optimization (Flyweight). These are the most specialized and least commonly applicable.
The fastest path to correct pattern selection is recognizing the type of problem you face. Different structural problem types map to different patterns.
Real-world problems often don't fit neatly into one category. 'I need to add logging to calls to an external service with an incompatible interface' could involve Adapter (interface), Decorator (logging), or Proxy (access control). In such cases, you may need to combine patterns—a topic we'll explore in detail later.
We've systematically compared all seven structural patterns across multiple dimensions. Let's consolidate the key selection criteria:
What's Next:
Now that you understand how to compare patterns, the next page provides a systematic Decision Tree for pattern selection. Given a problem description, you'll be able to navigate a structured framework that leads you to the appropriate pattern—or tells you when no pattern is needed.
You now have a comprehensive framework for comparing structural patterns. You understand their intents, mechanisms, trade-offs, and how to distinguish between commonly confused pairs. This foundation enables systematic pattern selection rather than intuition-based guessing.