100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Python

Adapter and Decorator Patterns

See how Adapter bridges incompatible interfaces and how Decorator adds behavior to objects dynamically without subclassing.

Design Patterns — Creational & StructuralIntermediate10 min readJul 8, 2026
Analogies

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

python
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

Was this page helpful?

Topics covered

#Python#SoftwareEngineeringStudyNotes#SoftwareEngineering#AdapterAndDecoratorPatterns#Adapter#Decorator#Patterns#Explanation#StudyNotes#SkillVeris