Hello! Welcome to the final lesson in our module on Structural Design Patterns.
Over the past six lessons, you have built a solid foundation by learning to apply the Adapter, Decorator, Facade, Proxy, Composite, and Bridge patterns. Now, it's time to bring all that knowledge together.
Today, we will focus on comparing these patterns to identify their unique use cases. Your learning outcome is to compare structural patterns and identify appropriate use cases for each. This is a critical skill for system design interviews, as choosing the right pattern demonstrates a deep understanding of design principles and trade-offs. While many of these patterns may seem structurally similar at first glance—often involving "wrapping" one object with another—their true difference lies in their intent.
A Bird's-Eye View of Structural Patterns
Before we dive into detailed comparisons, let's start with a high-level overview. Structural patterns are primarily concerned with how classes and objects are composed to form larger, more flexible structures.
This table provides a concise summary of the seven "Gang of Four" (GoF) structural patterns, including their core purpose and a typical use case.
Why You Should Master Design Patterns
To get a quick overview of all structural patterns at once, please review the 'Structural' section of the comparison table in the article 'Why You Should Master Design Patterns' on dev.to.
In the linked resource, find the table titled 'Tabular Comparison: All 23 Design Patterns at a Glance'. Focus only on the rows for the 'Structural' category: Adapter, Bridge, Composite, Decorator, Facade, Flyweight, and Proxy. This will give you a great starting point for comparison.
The image below offers another helpful visual summary of these patterns and their purposes.
.webp)
The Key to Differentiation: Intent vs. Structure
Many structural patterns use similar mechanisms, like object composition and interfaces, which can make them seem confusingly alike. The key to telling them apart is to always ask: What is the primary problem this pattern is trying to solve?
The following video by Christopher Okhravi provides an excellent, in-depth comparison of the most commonly confused structural patterns. We will use it as our guide for the next section.
Structural Patterns (comparison) – Design Patterns (ep 12)
This video, 'Structural Patterns (comparison) – Design Patterns (ep 12)', is a masterclass in distinguishing between patterns based on their intent. The presenter uses UML diagrams to compare their structure and then explains the subtle but crucial differences in their purpose.
Please watch from 09:56 to 35:18. This is a long segment, but it's the core of our lesson. I will break it down into smaller focus areas below. As you watch, pay close attention to the emphasis on 'intent'.
Let's break down the most important comparisons from the video.
1. The "Wrappers": Adapter, Decorator, and Proxy
These three patterns are frequently confused because they all involve one object acting as a go-between for another. However, their goals are completely different.
-
Adapter: Changes an interface.
- Intent: To make two incompatible interfaces work together. It acts as a translator. Think of a travel power adapter that lets your US plug fit into a European socket.
- As the video explains (around 12:20), the Adapter's focus is on resolving interface incompatibility, not changing the underlying behavior.
-
Decorator: Adds responsibilities.
- Intent: To attach additional behaviors to an object dynamically and transparently, without altering its interface. You can stack decorators like layers of an onion.
- The video contrasts this with Adapter by noting that Decorator is about extending behavior, often by combining multiple responsibilities, while keeping the interface consistent.
-
Proxy: Controls access.
- Intent: To provide a surrogate or placeholder for another object to control access to it. This control can be for many reasons, such as lazy initialization (a virtual proxy), access control (a protection proxy), or logging (a remote proxy).
- The video (around 19:20) highlights a key distinction: a Proxy has the same interface as its subject, whereas an Adapter has a different interface. The intent isn't to translate, but to manage the interaction with the real object.
The following resource provides a textual summary of these relationships.
To reinforce these distinctions, let's read the 'Related patterns' sections from the 'Structural Patterns' PDF. These concise comparisons are very effective.
Please read the following three short sections: Find the 'Adapter Pattern' section and read the subsection under 'Related patterns'. Find the 'Decorator pattern' section and read its 'Related patterns' subsection. Find the 'Proxy Pattern' section and read its 'Related patterns' subsection. Notice how they consistently differentiate the patterns based on interface change and intent (enhancing vs. controlling vs. adapting).
2. The Architectural Twins: Bridge vs. Adapter
Both patterns can seem similar because they involve redirecting calls from one object to another.
- Adapter: Is about making things work after the fact. You have two existing, incompatible classes, and you write an adapter to make them cooperate. It’s a tactical, reactive solution.
- Bridge: Is about designing a system for flexibility up-front. You intentionally separate an abstraction from its implementation so that you can vary both independently. It's a strategic, proactive design choice to prevent the "class explosion" we discussed in the last lesson.
As the video explains (around 27:30, though it's intertwined with Strategy), the Bridge is about creating two independent hierarchies of classes (e.g., Shape and Color), whereas the Adapter is usually a one-off connector.
3. The Simplifiers: Facade vs. Adapter
- Facade: Its purpose is to provide a single, simplified interface to a complex subsystem. It hides complexity and decouples clients from the intricate workings of a set of objects. A facade can wrap many objects; an adapter typically wraps one.
- Adapter: Its purpose is to convert one interface into another. It doesn't necessarily simplify anything; it just translates.
The video makes this distinction clear (around 14:15), noting that a Facade is meant to make a subsystem easier to use.
A Decision-Making Framework
In an interview, you'll need to justify your pattern choice. Here’s a simple thinking process to guide you:
- Start with the problem: What is the core design challenge you're facing?
- Ask clarifying questions based on pattern intents:
- Is the problem about integrating incompatible interfaces, possibly with a legacy system?
- Yes -> Adapter
- Is the problem about adding optional behaviors or responsibilities to an object at runtime?
- Yes -> Decorator
- Is the problem about managing or controlling access to an object (e.g., for security, performance, or remote access)?
- Yes -> Proxy
- Is the problem about simplifying the interface to a large and complex subsystem?
- Yes -> Facade
- Is the problem about designing a system from scratch where a concept and its implementation can both change independently (e.g., different shapes on different operating systems)?
- Yes -> Bridge
- Is the problem about representing a part-whole hierarchy where clients can treat individual objects and groups of objects uniformly?
- Yes -> Composite
- Is the problem about integrating incompatible interfaces, possibly with a legacy system?
Test your understanding!
For each scenario below, identify the most appropriate structural design pattern and briefly explain why.
-
You are building a user interface framework. You need to create complex UI elements like panels and windows, which can contain other UI elements (like buttons, text fields, or even other panels). You want your code to treat a simple button and a complex panel in the same way (e.g., when calling a
render()method). -
Your application needs to download large image files from a server. To improve performance, you don't want to download an image until it's actually displayed on the screen.
-
You're building an e-commerce platform. You need to calculate the final price of an order. The base price can be modified by various factors that the user can select: gift wrapping, express shipping, and insurance. Each of these adds a cost.
-
Your service needs to consume data from a new, third-party XML-based reporting service. Your existing application is built to process data in JSON format.
Show answer
-
Composite Pattern: The key here is the "part-whole hierarchy" (panels containing other elements) and the need to treat "individual objects and compositions uniformly." The Composite pattern is designed exactly for this, allowing you to build tree structures and have a single interface for both leaf nodes (Button) and composite nodes (Panel).
-
Proxy Pattern (Virtual Proxy): The goal is to control access to a resource (the large image) and delay its creation until needed. A Virtual Proxy is the perfect fit. It stands in for the real image object, and only when the client calls
display()on the proxy does it load the actual image from the server. -
Decorator Pattern: This is a classic Decorator scenario. You have a base object (the order with its base price) and you want to add responsibilities (costs for gift wrap, shipping, etc.) dynamically. You can create decorators like
GiftWrapDecoratorandExpressShippingDecoratorthat wrap the order object and augment itsgetPrice()method. -
Adapter Pattern: You have an existing system that expects a specific interface (JSON data) and a new component with an incompatible interface (XML data). The Adapter pattern is used to "adapt" the XML service's output into the JSON format your application understands, acting as a translator between the two.
Conclusion
Congratulations on completing the module on Structural Design Patterns! You've not only learned how each pattern works in isolation but also how to compare them and select the best one for the job.
Key Takeaways:
- Intent is King: The purpose of a pattern is its most important characteristic. Always start by asking what problem you are trying to solve.
- Adapter vs. Decorator vs. Proxy: Adapter changes the interface, Decorator adds behavior, and Proxy controls access.
- Bridge vs. Adapter: Bridge is a strategic, up-front design to decouple abstraction from implementation. Adapter is a tactical solution to make existing incompatible classes work together.
- Facade Simplifies: Use Facade to provide a simple, unified entry point to a complex subsystem.
- Composite Builds Trees: Use Composite to represent part-whole hierarchies and treat individual objects and groups uniformly.
In our next lesson, we will begin our journey into Behavioral Design Patterns, starting with the Observer pattern. While structural patterns focus on how objects are composed, behavioral patterns focus on how they communicate and assign responsibilities. This will open up a whole new set of tools for designing dynamic and interactive systems.