Design Patterns — Structural - Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy
The seven classic structural patterns (GoF) split cleanly into two intentions:
| Intention | Patterns |
|---|---|
| Make incompatible / unrelated things work together | Adapter, Bridge, Facade |
| Build / extend object trees and capabilities | Composite, Decorator, Proxy, Flyweight |

The 80/20: what actually matters
1. Adapter — translate an interface
WHY: You have a working class (a third-party library, legacy code) but its method names/signatures don't match what your code calls. You can't (or shouldn't) edit it.
WHAT: A wrapper exposing the target interface, internally calling the adaptee.
HOW (derive from scratch):
# Client wants this interface:
class MediaPlayer: # Target
def play(self, file): ...
# But we already have this incompatible class:
class LegacyMp4(object): # Adaptee
def start_mp4(self, path):
print("playing", path)
# Bridge the gap by FORWARDING with a translated name:
class Mp4Adapter(MediaPlayer):
def __init__(self, legacy):
self._legacy = legacy # composition: hold the adaptee
def play(self, file):
self._legacy.start_mp4(file) # translate play() -> start_mp4()Why this step? Mp4Adapter implements the interface the client speaks (play), so the client never knows a legacy class exists.
2. Bridge — separate abstraction from implementation
HOW:
class Color: # Implementation side
def fill(self): ...
class Red(Color):
def fill(self): return "red"
class Shape: # Abstraction side
def __init__(self, color): # holds a reference -> the "bridge"
self.color = color
def draw(self): ...
class Circle(Shape):
def draw(self):
return "Circle in " + self.color.fill()Why this step? Shape has-a Color (composition) instead of being a coloured shape (inheritance). Add a new colour → one class; add a new shape → one class.
3. Composite — treat one and many the same
WHY: Files & folders, GUI panels containing widgets, org charts — recursive part-whole hierarchies where you want render() or size() to work whether you call it on a leaf or a whole subtree.
HOW:
class Node:
def size(self): raise NotImplementedError
class File(Node): # Leaf
def __init__(self, kb): self.kb = kb
def size(self): return self.kb
class Folder(Node): # Composite
def __init__(self): self.children = []
def add(self, n): self.children.append(n)
def size(self):
return sum(c.size() for c in self.children) # recurse uniformlyWhy this step? Folder.size() calls size() on each child without checking its type — the recursion and the uniform interface are the whole point.
4. Decorator — add behaviour by wrapping
HOW:
class Coffee: # Component
def cost(self): return 2.0
class AddOn(Coffee): # Decorator base: wraps a Coffee, IS a Coffee
def __init__(self, drink): self.drink = drink
class Milk(AddOn):
def cost(self): return self.drink.cost() + 0.5 # forward + add
class Sugar(AddOn):
def cost(self): return self.drink.cost() + 0.2
order = Sugar(Milk(Coffee())) # stackable: 2.0 + 0.5 + 0.2 = 2.7Why this step? Milk.cost() calls the wrapped object first, then adds its own bit — that "call inner, then extend" is what makes decorators composable.
5. Facade — one simple door to a complex subsystem
WHY: Starting a computer means CPU, Memory, Disk all cooperating. The client just wants start().
HOW:
class Facade:
def __init__(self): self.cpu, self.mem, self.disk = CPU(), Mem(), Disk()
def start(self):
self.cpu.freeze(); self.mem.load(); self.disk.read(); self.cpu.jump()Why this step? The facade orchestrates the subsystem so the client makes one call instead of four ordered ones. Note: Facade doesn't hide the subsystem — advanced clients can still reach in.
6. Flyweight — share to save memory
HOW:
class GlyphFactory:
_pool = {}
def get(self, char):
if char not in self._pool:
self._pool[char] = Glyph(char) # create once, reuse forever
return self._pool[char] # SHARED instance
# extrinsic state (x,y) passed at draw time, not stored in GlyphWhy this step? The factory caches by intrinsic key so repeated requests return the same object — that sharing is the memory win.
7. Proxy — a stand-in that controls access
HOW:
class RealImage:
def __init__(self, f): self._load(f) # expensive!
def display(self): ...
class ProxyImage:
def __init__(self, f): self.f, self.real = f, None
def display(self):
if self.real is None: # lazy: load only on first use
self.real = RealImage(self.f)
self.real.display()Why this step? The proxy defers the expensive construction until display() is actually called — same interface, controlled access.
Recall Feynman: explain to a 12-year-old
Imagine LEGO. Adapter is a little brick that connects two LEGO sets that normally don't click together. Decorator is putting stickers and lights on a car — each one adds something, and you can keep adding more. Composite is a box of LEGO that you can carry as one thing, even though it has tiny pieces inside, and boxes can hold boxes. Facade is one big red "GO" button that secretly does ten steps for you. Proxy is a guard who stands in front of a treasure room and only opens the door when you're really allowed in. Flyweight is using the same picture of a tree a thousand times in a video game instead of drawing a thousand trees. Bridge is keeping "shape" and "colour" as two separate sticker sheets so you mix any shape with any colour without making every combo by hand.
Flashcards
What problem does Adapter solve?
Adapter vs Bridge — core difference?
Why does Bridge reduce a class explosion?
Key property that lets Decorators stack?
Decorator vs Proxy — intent?
What makes Composite powerful?
Does Facade hide the subsystem completely?
Flyweight: intrinsic vs extrinsic state?
What are the four common Proxy types?
The single sentence unifying most structural patterns?
Connections
- Composition over Inheritance — the principle behind Adapter, Bridge, Decorator, Proxy
- Open-Closed Principle — Decorator & Bridge extend behaviour without modifying code
- Single Responsibility Principle — Facade isolates orchestration logic
- Creational Patterns — Flyweight relies on a Factory to pool shared objects
- Behavioral Patterns — Composite often pairs with Visitor / Iterator over the tree
- Dependency Inversion — Bridge depends on the implementation interface, not concretes
- Recursion — Composite's uniform
size()/render()is recursive descent
Concept Map
Hinglish (regional understanding)
Intuition Hinglish mein samjho
Structural patterns ka simple matlab hai: tumhare paas objects hain, ab unhe aapas me kaise jodna (wire karna) hai taaki pura system flexible rahe aur loosely coupled rahe. Yaad rakhne ka shortcut — zyadatar structural pattern me ek object dusre object ka reference rakhta hai aur calls ko forward kar deta hai (yeh hai "composition over inheritance"). Bas intent badalta hai: Adapter interface ko translate karta hai, Decorator behaviour add karta hai (aur stack ho sakta hai), Proxy access control karta hai (lazy load, permission), Facade kai subsystems ko ek simple call ke peeche chhupa deta hai.
Composite tree banata hai — File aur Folder dono ka same interface, isliye size() recursively kaam karta hai chahe ek file ho ya pura folder. Bridge bohot important hai interview ke liye: agar Shape × Color ko inheritance se banao to classes ban jaati hain (RedCircle, BlueSquare...). Bridge ise kar deta hai kyunki Shape ke paas Color ka reference hota hai. Flyweight memory bachata hai — game me 1 million tree draw karne hain par sirf 3 alag mesh objects share karke.
Confusion points jo exam me aate hain: Adapter vs Bridge — Adapter purani incompatible cheez ko baad me fit karta hai; Bridge pehle se design hota hai taaki do dimensions independently vary karein. Decorator vs Proxy — dono wrap karte hain, par Decorator feature add karta hai, Proxy access control karta hai.