Introduction
Adapter and Decorator are Structural design patterns — they are concerned with how objects are composed, not with how they communicate or how they are created. Adapter converts the interface of an existing class into another interface that client code expects, allowing classes with incompatible interfaces to work together. Decorator attaches additional responsibilities to an object dynamically, providing a flexible alternative to subclassing for extending behavior.
Cricket analogy: When Rohit Sharma opens with an associate-nation partner who signals differently, a translating non-striker (Adapter) bridges calls, while extra kit like an arm sleeve added mid-innings (Decorator) layers new capability onto the same batter without changing who he is.
Explanation
Adapter is typically used when integrating a third-party library or legacy code whose interface does not match what your application expects. Rather than modifying the third-party code (which may be impossible) or rewriting all call sites, you write a thin Adapter class that wraps the incompatible object and translates calls between the two interfaces. This keeps the incompatible dependency isolated behind a boundary your code controls.
Cricket analogy: When Chennai Super Kings sign a T20 specialist from the Big Bash whose training methods differ, they don't rewrite his technique — they assign an analyst who translates his metrics into the team's own system, isolating the mismatch.
Decorator wraps an object with another object that implements the same interface, forwarding calls to the wrapped object while adding extra behavior before or after the call. Because decorators implement the same interface as the object they wrap, decorators can be stacked — wrapping a decorator in another decorator — to compose behavior flexibly at runtime. This avoids an explosion of subclasses that would otherwise be needed to cover every combination of features (e.g., CoffeeWithMilk, CoffeeWithMilkAndSugar, CoffeeWithSugar, and so on).
Cricket analogy: Instead of creating separate player profiles for 'opener,' 'opener-with-powerplay-role,' and 'opener-with-powerplay-and-death-overs-role,' a captain layers responsibilities onto Rohit Sharma one at a time, each layer adding duties while he's still fundamentally an opening batter.
Example
from abc import ABC, abstractmethod
# --- Adapter: reconcile two incompatible interfaces ---
class EuropeanSocket:
def voltage(self):
return 230
class USASocketInterface(ABC):
@abstractmethod
def us_voltage(self):
...
class EuropeanToUSAdapter(USASocketInterface):
def __init__(self, european_socket: EuropeanSocket):
self._socket = european_socket
def us_voltage(self):
# Adapt: convert European voltage reading to expected US-style value
return round(self._socket.voltage() * (120 / 230))
# --- Decorator: add behavior dynamically without subclassing ---
class Coffee(ABC):
@abstractmethod
def cost(self):
...
@abstractmethod
def description(self):
...
class SimpleCoffee(Coffee):
def cost(self):
return 2.0
def description(self):
return "Coffee"
class CoffeeDecorator(Coffee):
def __init__(self, wrapped: Coffee):
self._wrapped = wrapped
def cost(self):
return self._wrapped.cost()
def description(self):
return self._wrapped.description()
class MilkDecorator(CoffeeDecorator):
def cost(self):
return self._wrapped.cost() + 0.5
def description(self):
return self._wrapped.description() + " + milk"
class SugarDecorator(CoffeeDecorator):
def cost(self):
return self._wrapped.cost() + 0.2
def description(self):
return self._wrapped.description() + " + sugar"
if __name__ == "__main__":
adapter = EuropeanToUSAdapter(EuropeanSocket())
print(adapter.us_voltage()) # ~120
order = SugarDecorator(MilkDecorator(SimpleCoffee()))
print(order.description(), "=", order.cost())Analysis
EuropeanToUSAdapter implements USASocketInterface (the interface the client expects) while internally delegating to and translating values from EuropeanSocket (the incompatible interface it wraps). No changes were needed to EuropeanSocket itself. In the Decorator example, MilkDecorator and SugarDecorator both implement the same Coffee interface as SimpleCoffee, and each wraps another Coffee object, forwarding the call and layering on extra cost and description text. Wrapping MilkDecorator(SimpleCoffee()) inside SugarDecorator(...) composes both behaviors at runtime without ever creating a SimpleCoffeeWithMilkAndSugar subclass.
Cricket analogy: Picture a plug converter shaped exactly like a UK three-pin socket (the interface expected) that internally reroutes an Indian round-pin device's power — similarly, a translator umpire who 'looks like' a local umpire to broadcast systems while internally interpreting a foreign captain's DRS calls.
The key distinction: Adapter changes an interface to make two incompatible things compatible (translation), whereas Decorator preserves the interface but augments behavior (enhancement). Both are Structural patterns because they are about composing objects — not about creation (Creational) and not about runtime message-passing protocols between many collaborators (Behavioral). A frequent mistake is describing Decorator as a Behavioral pattern because it 'adds behavior' — but the mechanism is object composition/wrapping, which is why the GoF classifies it as Structural.
Cricket analogy: An Adapter is like a stump-mic converter that changes audio format for broadcasters; a Decorator is like a batter adding a helmet grille without changing how he's scored — both are equipment arrangement (Structural), not drafting a new player (Creational) or ball-by-ball fielder signaling (Behavioral); commentators sometimes wrongly call added kit a 'tactic' when it's really gear composition.
Key Takeaways
- Adapter converts an existing interface into one that client code expects, enabling incompatible classes to work together.
- Decorator adds responsibilities to an object dynamically by wrapping it, without altering its class or using subclassing.
- Decorators implement the same interface as the object they wrap, so they can be stacked to combine behaviors.
- Both Adapter and Decorator are Structural patterns, not Behavioral, because they concern object composition.
- Adapter is about translation between interfaces; Decorator is about enhancement while preserving the interface.
Practice what you learned
1. What is the primary purpose of the Adapter pattern?
2. Why can Decorators be stacked on top of one another?
3. Adapter and Decorator both belong to which GoF pattern category?
4. What is the key functional difference between Adapter and Decorator?
Was this page helpful?
You May Also Like
Design Patterns Overview
An introduction to the Gang of Four design patterns and their three categories: Creational, Structural, and Behavioral.
Facade and Proxy Patterns
Learn how Facade simplifies access to a complex subsystem and how Proxy controls access to another object via a shared interface.
SOLID Principles Overview
A guided tour of the five SOLID design principles that make object-oriented code easier to maintain and extend.
Liskov Substitution and Interface Segregation
Understand why subtypes must honor their base type's contract and why fat interfaces should be split into focused ones.
Related Reading
Related Study Notes in Software Engineering
Browse all study notesMicroservices Study Notes
Software Architecture · 30 topics
Software EngineeringTesting & TDD Study Notes
Software Testing · 30 topics
Software EngineeringDesign Patterns Study Notes
Software Design · 30 topics
Software EngineeringGit & Version Control Study Notes
Bash · 40 topics
Software EngineeringSystem Design Study Notes
Architecture · 40 topics