Visual walkthrough — Design Patterns — Structural - Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy
Before any code, three plain words we will keep reusing:
Everything below is the wrapper, redrawn with a different job written on it. See Composition over Inheritance for why "holding a reference" beats "inheriting from".
Step 1 — The bare call (no pattern yet)
WHAT: A client calls a method on a subject directly.
WHY: We need the before picture. Every pattern is a modification of this one arrow, so we must see the arrow with nothing on it first.
PICTURE: One box (Client) points at another box (Subject). The label on the arrow is the method name.

- — the message. The client sends it; the subject runs it.
- The arrow is a direct dependency: if the subject's method name changes, the client breaks. That fragility is the problem every following step fixes in a different way.
Step 2 — Insert the wrapper (the universal shape)
WHAT: Put a third box between client and subject. The client now calls the wrapper; the wrapper calls the subject.
WHY: A middle box gives us a place to do something on the way through — translate, add, block, or fan out. Nothing here yet does anything but pass the call, but the empty seat is the birthplace of all seven patterns.
PICTURE: Two arrows in a row. The wrapper holds the subject (composition), shown by a small "has-a" line.

class Wrapper:
def __init__(self, subject):
self._subject = subject # HOLD it (composition, not inheritance)
def do(self):
return self._subject.do() # FORWARD: pure pass-through, adds nothing yetself._subject— the held reference. This single line is the "has-a" that replaces "is-a".return self._subject.do()— the forward. Read it as: "whatever I was asked, ask the same of the thing I hold."
Now we differ only by what we insert around that forward line. Each following step edits one method body.
Step 3 — Twist the name: Adapter
WHAT: The subject's method is named differently (start_mp4, say). The wrapper forwards, but renames the call.
WHY: The client speaks one vocabulary (play); a legacy/third-party subject speaks another (start_mp4). We can't edit the subject. So the wrapper translates the interface. This obeys Open-Closed Principle — new class, zero edits to old code.
PICTURE: Same two arrows, but the second arrow carries a different label than the first — a little translator sitting on the wire.

# Target: the interface the CLIENT speaks.
class MediaPlayer:
def play(self, file):
raise NotImplementedError
# Adaptee: a legacy class we cannot edit; different method name.
class LegacyMp4:
def start_mp4(self, path):
print("playing", path)
# Adapter: IS a MediaPlayer, HOLDS a LegacyMp4, translates the call.
class Mp4Adapter(MediaPlayer): # speaks the CLIENT's language: play()
def __init__(self, legacy):
self._legacy = legacy # composition: hold the adaptee
def play(self, file):
self._legacy.start_mp4(file) # SAME job, DIFFERENT name -> translationclass MediaPlayer— the target interface: what the client already calls (play).class LegacyMp4— the adaptee: works, but namedstart_mp4; we may not change it.class Mp4Adapter(MediaPlayer)— it is what the client expects, so the client never learns a legacy class exists.play(...)in,start_mp4(...)out — the two labels differ. That difference is the whole pattern.
Recall Quick self-test (reveal the answer)
Reveal lines below use the vault convention Question ::: Answer — read up to the :::, answer in your head, then check what follows it.
Adapter changes the ______ of the forwarded call ::: the name / interface (the method signature), not the behaviour.
Step 4 — Twist the behaviour: Decorator
WHAT: Keep the same method name in and out, but do the subject's work and add a little of your own.
WHY: We want to add features (milk, sugar, logging, caching) at runtime without a class per combination. Because the name matches, a decorated object is interchangeable with a plain one — so wrappers stack.
PICTURE: The forward arrow keeps its name, but a little "+extra" tag hangs off the wrapper. Two wrappers chain: Sugar → Milk → Coffee, each adding its bit.

# Component: the base object and the shared interface, cost().
class Coffee:
def cost(self):
return 2.0
# Decorator base: IS a Coffee, HOLDS a Coffee. Adds nothing by itself.
class AddOn(Coffee):
def __init__(self, drink):
self.drink = drink # composition: hold the wrapped drink
# Concrete decorators: forward FIRST, then add their own bit.
class Milk(AddOn):
def cost(self):
return self.drink.cost() + 0.5
class Sugar(AddOn):
def cost(self):
return self.drink.cost() + 0.2
order = Sugar(Milk(Coffee())) # 2.0 + 0.5 + 0.2 = 2.7class Coffee— the component, and the interface everyone shares:cost().class AddOn(Coffee)— the decorator base; being aCoffeeand holding one is what lets wrappers stack.self.drink.cost()— forward, same name (cost→cost). This is what lets layers stack.+ 0.5— the added responsibility. Adapter renamed; Decorator augments.Sugar(Milk(Coffee()))— three layers, evaluated inside-out:2.0then+0.5then+0.2.
Step 5 — Twist the access: Proxy
WHAT: Same name in and out, but the wrapper decides whether, when, or how often to forward — lazy-loading, caching, or permission checks.
WHY: Sometimes the real work is expensive (load a huge image) or restricted (needs a login). The proxy controls access to a single fixed subject instead of always calling straight through.
PICTURE: The forward arrow now passes through a gate. On the first call the gate builds the subject; on later calls it may short-circuit (cache) or block.

# The real, expensive subject. Building it loads a big file from disk.
class RealImage:
def __init__(self, path):
self._path = path
print("loading", path) # the costly work we want to defer
def display(self):
print("showing", self._path)
# Proxy: same interface (display), but GATES when RealImage is built.
class ImageProxy:
def __init__(self, path):
self._path, self._real = path, None
def display(self):
if self._real is None: # GATE: only build on real need
self._real = RealImage(self._path) # lazy: created on first use
return self._real.display() # then forwardclass RealImage— the real subject: heavy to build, cheap to reuse once built.if self._real is None:— the gate. Direct call has no gate; proxy's whole job is thatif.- Proxy wraps one fixed subject and does not aim to stack — that separates it from Decorator, whose gate would instead be a
+ extra.
Step 6 — Twist the count: Composite
WHAT: The wrapper holds many subjects (a list of children) and forwards the call to all of them, treating one and many identically.
WHY: Files/folders, panels/widgets, org charts — recursive part-whole trees. We want size() to work whether we call it on a single leaf or a whole subtree, without the client checking types. This is Recursion made structural.
PICTURE: One box fans out into a tree. A leaf answers directly; a folder asks each child and sums. The same size() arrow points at both.

Recall the (sum) sign introduced up top — "add one value per child":
# Component: the ONE shared interface every node must provide.
# Both leaves and composites are Nodes, so the client can call size()
# on any of them without ever asking "which kind are you?".
class Node:
def size(self):
raise NotImplementedError # every Node promises a size()
class File(Node): # leaf: no children, answers directly
def __init__(self, kb):
self.kb = kb
def size(self):
return self.kb
class Folder(Node): # composite: holds children, sums them
def __init__(self):
self.children = []
def add(self, node):
self.children.append(node)
def size(self):
return sum(c.size() for c in self.children) # forward to MANY, uniformlyclass Node— the component: the common interface that promises asize(). Because bothFileandFolderareNodes, the client treats one and many alike.- — forward to every child. Count went from one to many.
c.size()never asks "are you a File or Folder?" — the uniform interface is the point, and the recursion bottoms out at leaves.- Empty folder —
children = []makes the sum ; the recursion terminates cleanly with no special case. An empty tree is just a node with a bottom already reached.
Recall
What makes Composite recursion terminate? ::: leaves return a value directly (no children to recurse into), so the tree has a bottom.
Step 7 — Twist into two dimensions: Bridge
WHAT: Split one concept into two hierarchies — an abstraction and an implementation — each holding a reference to the other's side, so they vary independently.
WHY: To dodge a class explosion. Shapes () × colours () by inheritance needs classes; a bridge (has-a) needs only . This is Dependency Inversion in miniature: the high-level Shape depends on a Color abstraction, not a concrete colour.
PICTURE: Two parallel ladders (Shape side, Color side) joined by one horizontal reference — the "bridge".

# Implementation side. This is the interface every colour must provide:
# a fill() that returns the colour name. Concrete colours implement it.
class Color:
def fill(self):
raise NotImplementedError # every Color promises fill()
class Red(Color):
def fill(self):
return "red"
# Abstraction side. Shape declares its own interface: a draw().
# It HOLDS a Color and forwards across the bridge to color.fill().
class Shape:
def __init__(self, color):
self.color = color # the reference across the bridge
def draw(self):
raise NotImplementedError # every Shape promises draw()
class Circle(Shape):
def draw(self):
return "Circle in " + self.color.fill() # forward across the bridgeclass Colorwithfill()— the implementation interface: what every colour must provide.class Shapewithdraw()— the abstraction interface: what every shape must provide. It holds aColor(has-a) instead of being a coloured shape (is-a).- Add a shape → one class; add a colour → one class. That is the saving.
Step 8 — Twist the door: Facade
WHAT: The wrapper forwards to many unrelated subsystems behind one simple call, orchestrating their order.
WHY: Starting a computer means CPU, Memory, and Disk cooperating in a fixed sequence. The client just wants start() — it should not know the four ordered steps. Facade simplifies by owning that orchestration. See Single Responsibility Principle: the client's one responsibility is "start", not "sequence the subsystems".
PICTURE: One door (the facade) fans out to several different subsystem boxes; the client only ever touches the door.

The arrow below simply means "then do the next thing":
class ComputerFacade:
def __init__(self):
self.cpu, self.mem, self.disk = CPU(), Mem(), Disk()
def start(self):
self.cpu.freeze() # one call fans out to
self.mem.load() # several DIFFERENT subsystems,
self.disk.read() # in a fixed ORDER the client
self.cpu.jump() # never has to know- Unlike Composite (many children of the same interface, summed), Facade drives different subsystems in sequence.
- The facade does not hide the subsystem — an advanced client can still reach
cpu,mem,diskdirectly.
Step 9 — Twist toward sharing: Flyweight
WHAT: Instead of every object owning its heavy data, many objects share one copy of the intrinsic part; the extrinsic part is handed in per call.
WHY: A text editor with a million characters would waste memory storing the glyph shape of 'a' a million times. Since that shape is identical everywhere, keep one shared copy. You end up with 26 glyph objects for the lowercase alphabet, not a million.
PICTURE: A small shared pool of glyph objects; many draw-calls point at the same glyph but carry their own .

# The flyweight itself: holds ONLY intrinsic state (the shared shape).
class Glyph:
def __init__(self, char):
self.char = char # intrinsic: identical for every 'a'
def draw(self, x, y): # extrinsic (x, y) PASSED IN, not stored
print(self.char, "at", x, y)
# Factory hands out shared flyweights, creating each at most once.
class GlyphFactory:
_pool = {} # the share table: char -> one Glyph
def get(self, char):
if char not in self._pool:
self._pool[char] = Glyph(char) # create once,
return self._pool[char] # reuse the SHARED instance
factory = GlyphFactory()
a1 = factory.get("a")
a2 = factory.get("a") # SAME object as a1 (shared)
a1.draw(1, 2) # extrinsic position passed at draw time
a2.draw(9, 3) # same glyph, different positionclass Glyph— the flyweight: it stores only the intrinsicchar, never a position._pool— the share table: oneGlyphper distinct character, reused forever.a1anda2are the same object.draw(self, x, y)— the extrinsic arrives as arguments, so a million positions cost no extra glyph objects.
Recall
Which half of an object's state does Flyweight share, and which does it pass in? ::: it shares the intrinsic state (identical across objects), and passes in the extrinsic state (differs per object).
The one-picture summary
WHAT: All seven patterns are the Step-2 wrapper with one thing twisted. This final figure lays them side by side, each labelled by the twist.

| Twist on the forward | Pattern | One-word job |
|---|---|---|
| Rename the call | Adapter | translate |
| Same name + extra, stackable | Decorator | augment |
| Gate the call | Proxy | control |
| Forward to many, uniformly | Composite | tree |
| Two hierarchies referencing | Bridge | de-explode |
| Fan to subsystems, one door | Facade | simplify |
| Share one, pass the rest in | Flyweight | save memory |
Recall Feynman retelling — say it to a 12-year-old
Imagine you want to ask your friend a question, but you go through a helper standing in the doorway. That helper is a wrapper: they take your question and pass it on. Now change the helper's rule: — If they translate your question into your friend's language, that's Adapter. — If they answer, then add a joke of their own, that's Decorator — and you can line up several helpers, each adding a joke. — If they check whether you're allowed in first, or remember last time's answer, that's Proxy. — If they pass your question to a whole room of friends and add up the replies, that's Composite. — If your friends come in two kinds — who they are and what colour shirt they wear — and you mix-and-match freely instead of naming every combination, that's Bridge. — If the helper does four ordered chores for you when you just say "start", that's Facade. — If a million people all reuse one shared drawing of the letter A and only carry their own position, that's Flyweight. One doorway, seven different rules pinned above it. Learn the doorway once; the rest are just the sign on the wall.
Back to the parent: the Hinglish overview. Related principles: Single Responsibility Principle, Creational Patterns, Behavioral Patterns.