2.2.12 · Coding › Design Principles
Intuition Ek sentence mein
behavioral patterns ki soul
Creational patterns poochte hain "main objects kaise banaun?" , structural patterns poochte hain "main objects kaise compose karun?" , aur behavioral patterns poochte hain "objects aapas mein baat kaise karte hain aur responsibility kaise baantte hain?" — yeh runtime par communication aur control ke flow ke baare mein hain.
Jab do pieces of code ek doosre ke kaam karne ke tarike ke baare mein zyada jaante hain, toh woh aapas mein jud jaate hain. Ek ko change karo, doosre ko bhi change karna padta hai. Behavioral patterns collaborators ke beech ek patla, agreed-upon contract (ek interface ya ek message) daalta hai taaki kaun-kya-karta-hai alag-alag vary kar sake kaun-kisko-bulata-hai se.
Har pattern mein ek hi repeated trick hai: ek hard-coded if/switch ya direct method call ko ek aisi object se replace karo jise tum swap, store, queue, ya notify kar sako.
Definition Quick contracts
Observer ::: ek subject apna state change hone par bahut saare dependents ko automatically notify karta hai (publish/subscribe).
Strategy ::: interchangeable algorithms ko ek interface ke peechhe encapsulate karo; runtime par pick karo.
Command ::: ek request ko object mein convert karo (taaki use queue, log, ya undo kar sako).
Iterator ::: collection ka internal structure expose kiye bina collection ko traverse karo .
State ::: ek object ko apna behavior tab change karne do jab uski internal state change ho (lagta hai jaise class badal gayi ho).
Template Method ::: base method mein algorithm ka skeleton define karo , steps ko subclasses par defer karo.
Chain of Responsibility ::: ek request ko handlers ki chain ke along pass karo jab tak koi handle na kar le.
Mediator ::: ek central object bahut saare colleagues ke beech communication coordinate karta hai (many-to-many → star).
Memento ::: encapsulation tod bina kisi object ki internal state capture aur restore karo (snapshots).
Visitor ::: classes ko modify kiye bina class hierarchy mein new operations add karo .
Ek spreadsheet cell, ek chart, aur ek status bar sab ko tab update karna hota hai jab data change hota hai. data.update(); chart.update(); bar.update() hard-code karne ka matlab hai ki data layer ko UI ke baare mein pata hona chahiye. Bad. Iske badle data "main change hua" broadcast karta hai aur jo sunna chahta hai woh sunta hai.
Subject Observers ki ek list rakhta hai. State change par woh notify() call karta hai, jo loop mein observer.update() call karta hai. Observers runtime par subscribe/unsubscribe karte hain . Subject observers ko sirf ek interface ke through jaanta hai, unki concrete class ke through nahi.
Worked example Minimal Observer (Python) —
Har step kyun?
class Subject :
def __init__ (self):
self ._observers = [] # Why: subject list ka maalik hai, observers nahi
def subscribe (self, o): self ._observers.append(o)
def unsubscribe (self, o): self ._observers.remove(o)
def notify (self, data):
for o in self ._observers: # Why: loop = decoupled fan-out
o.update(data) # Why: interface method call karta hai, concrete code nahi
class Logger :
def update (self, data): print ( "LOG:" , data)
s = Subject(); s.subscribe(Logger())
s.notify( "temp=42" ) # LOG: temp=42
Return value kyun nahi? Observer push hai; subject jawab ka intezaar nahi karta aur na hi uski parwah hai.
Common mistake Steel-man: "Model se seedha UI call karo, zyada simple hai."
Yeh simple lagta hai kyunki do objects ke liye kam lines hain. Fix yeh hai: yeh scale nahi hota — har naya observer model ko edit karta hai, aur model ko UI ke bina unit-test nahi kiya ja sakta. Observer 5 lines ki plumbing ke badle new listeners add karne ki O(1) cost deta hai.
Tumhare paas ek task hai (sort, compress, route) aur bahut saare algorithms . Ek bada if mode == "quick": ... elif mode == "merge": ... ek maintenance bomb hai. Strategy har algorithm ko ek swappable object banata hai.
Ek Context ek Strategy interface ka reference rakhta hai aur strategy.execute() par delegate karta hai. Concrete strategies ise alag-alag implement karti hain. Client strategy choose karta hai; context kabhi nahi jaanta kaun si hai.
class Context :
def __init__ (self, strategy): self .strategy = strategy
def do (self, data): return self .strategy(data) # delegate, no if/else
Context( sorted ).do([ 3 , 1 , 2 ]) # ascending
Context( lambda d: sorted (d, reverse = True )).do([ 3 , 1 , 2 ]) # descending
Function kyun pass karo? Python mein ek function hi ek strategy object hai — minimal Strategy ko class ki zaroorat nahi.
Common mistake "State aur Strategy identical lagte hain — same UML!"
Sach hai, structure twins hain. Fix intent mein hai: Strategy ke objects independent hote hain, client dwara pick kiye jaate hain aur ek doosre ke baare mein nahi jaante. State ke objects next state jaante hain aur transitions khud trigger karte hain .
Ek button ke click handler mein yeh logic nahi hona chahiye ki woh kya karta hai — warna tum use undo, queue, schedule, ya log nahi kar sakte. "Jo karna hai usse" ek object mein wrap karo jisme execute() (aur undo()) ho.
Ek Command object receiver , method , aur arguments store karta hai. Ek Invoker (button, queue) bas command.execute() call karta hai. Yeh ek verb ko noun mein convert karta hai , undo/redo, macros, aur job queues enable karta hai.
class AddText :
def __init__ (self, doc, text): self .doc, self .text = doc, text
def execute (self): self .doc.append( self .text)
def undo (self): self .doc.pop() # Why: reverse karne ke liye kaafi store karo
history = []
def run (cmd): cmd.execute(); history.append(cmd)
def undo (): history.pop().undo()
history list kyun rakhein? Kyunki commands ab first-class objects hain, inhe stack kar sakte ho — literally Ctrl-Z yahi hai.
Tum chahte ho ki for x in collection same kaam kare chahe woh tree ho, list ho, ya database cursor — yeh expose kiye bina ki woh array hai ya linked nodes.
Ek Iterator next() / has_next() expose karta hai (Python mein __next__ + StopIteration). Collection ek iterator produce karta hai __iter__ se. Traversal state iterator mein hoti hai, collection mein nahi , isliye multiple traversals independently chalti hain.
class Countdown :
def __init__ (self, n): self .n = n
def __iter__ (self): # Why: fresh iterator return karta hai
i = self .n
while i > 0 :
yield i # Why: generator = iterator for free
i -= 1
list (Countdown( 3 )) # [3, 2, 1]
Ek document Draft → Moderation → Published hota hai. Har jagah if self.status == ... code karne se transition rules duplicate hoti hain. State har status ke behavior ko apni class mein dalta hai.
Context behavior ko current State object par delegate karta hai. Har state next state decide karta hai aur context ko switch karne ko kehta hai . Ek status add karna = ek class add karna, na ki har method edit karna.
class Draft :
def publish (self, ctx): ctx.state = Published()
class Published :
def publish (self, ctx): print ( "already live" )
class Doc :
def __init__ (self): self .state = Draft()
def publish (self): self .state.publish( self )
ctx kyun pass karo? Taaki state context ka state field mutate kar sake — yahi transition hai.
Do algorithms ke 80% steps same hain lekin ek mein alag hain. Copy-paste mat karo; base class mein skeleton freeze karo aur subclasses ko holes fill karne do.
Ek base class ek final method define karta hai jo abstract hook steps ko fixed order mein call karta hai. Subclasses steps override karti hain, order kabhi nahi. (Inversion of control: "don't call us, we'll call you.")
class Report :
def generate (self): # template — fixed skeleton
self .header(); self .body(); self .footer()
def header (self): print ( "=== Report ===" )
def body (self): raise NotImplementedError # hook
def footer (self): print ( "--- end ---" )
class Sales ( Report ):
def body (self): print ( "sales: 100" )
Strategy vs Template Method? Strategy ek algorithm ko composition se vary karta hai (ek object jo pass kiya jata hai); Template Method inheritance se (ek method override karo).
Ek support ticket L1 handle kar sakta hai, L2 par escalate ho sakta hai, phir manager tak. Sender ko nahi pata hona chahiye ki aakhir mein kaun handle karta hai.
Har Handler ka ek next link hota hai. Woh ya toh request handle karta hai ya ==use next par pass kar deta hai==. Chain wahan rukti hai jahan pehla handler succeed karta hai (ya end tak pahunch jaata hai).
class Handler :
def __init__ (self, limit, nxt = None ): self .limit, self .nxt = limit, nxt
def handle (self, amount):
if amount <= self .limit: print ( f "approved by {self .limit } -handler" )
elif self .nxt: self .nxt.handle(amount) # Why: aage pass karo
else : print ( "rejected" )
chain = Handler( 100 , Handler( 1000 ))
chain.handle( 500 ) # approved by 1000-handler
Agar 5 UI widgets sab ek doosre ko reference karein, toh potentially 5×4 = 20 connections ho sakte hain — ek tangled mesh. Sab kuch ek mediator se route karo aur yeh ek clean star ban jaata hai (5 connections).
Colleagues seedhe ek doosre se baat nahi karte; woh events ek Mediator ko bhejte hain , jo decide karta hai baaki kise notify karna hai. Ek many-to-many web ko hub-and-spoke mein convert karta hai. (Note: Observer generic broadcast hai; Mediator specific coordination logic encode karta hai.)
Undo support karne ke liye tumhe kisi object ki private state ka snapshot chahiye — lekin un privates ko expose karna encapsulation tod deta hai. Memento snapshot ko ek opaque box mein store karta hai jise sirf originator padh sakta hai.
Originator ek Memento create karta hai jo uski state rakhta hai; ek Caretaker mementos store karta hai (undo stack) bina kabhi unhe inspect kiye . Restore = originator.restore(memento).
class Editor :
def __init__ (self): self .text = ""
def save (self): return self .text # the memento (caretaker ke liye opaque)
def restore (self, m): self .text = m
e = Editor(); e.text = "hi" ; snap = e.save()
e.text = "bye" ; e.restore(snap) # back to "hi"
Memento vs Command-undo? Command yaad rakhta hai kisi action ko reverse kaise karein ; Memento yaad rakhta hai poori state . Memento tab use karo jab reversing mushkil ho lekin snapshotting sasti ho.
Tumhare paas ek stable class hierarchy hai (Circle, Square, Triangle) aur tum operations (area, draw, export) badhate rehte ho. Har operation ko har class mein add karna invasive hai. Visitor operations ko bahar visitor objects mein le jaata hai.
Har element accept(visitor) implement karta hai jo visitor.visitX(self) call karta hai — yeh double dispatch sahi method pick karta hai element type aur visitor type dono se. ==Naya operation add karna = naya Visitor class add karna, zero element classes ko touch karna.==
class Circle :
def accept (self, v): return v.visit_circle( self ) # double dispatch
class AreaVisitor :
def visit_circle (self, c): return 3.14159 * c.r ** 2
Trade-off: Visitor operations add karna easy karta hai lekin naya element type add karna mushkil (har visitor ko change karna padta hai). Yeh normal OOP ka mirror image hai.
Intuition Agar sab bhool jao
Observer + Strategy + Command real-world usage ka ~80% hain. Inhein teeno bilkul pakke se master karo.
Har behavioral pattern = ek hard-coded call/if ko ek swappable object se replace karo .
Intent se look-alikes: Strategy vs State (client-picks vs self-transitions), Strategy vs Template Method (composition vs inheritance), Mediator vs Observer (specific coordination vs generic broadcast), Memento vs Command-undo (full snapshot vs reversible action).
"On Saturday, CICS, To Catch More Mistakes, Visit."
O bserver · S trategy · C ommand · I terator · C (State→"Change") · T emplate · C hain · M ediator · M emento · V isitor.
Recall Feynman: ek 12 saal ke bacche ko samjhao
Ek classroom imagine karo.
Observer : teacher ghanti bajata hai aur jo sunna chahte hain woh sab react karte hain. (broadcast)
Strategy : tum ek maths problem addition se solve kar sakte ho, ya ungliyon par gin ke — tum method choose karte ho aur problem ko koi fark nahi.
Command : tum ek chore sticky note par likhte ho ("kachra bahar karo"). Ab tum ise baad mein kisi ko de sakte ho, ya faad sakte ho (undo).
Iterator : ek remote jisme "next channel" button hai — tumhe nahi pata TV channels kaise store karta hai.
State : ek traffic light apne colour ke hisaab se alag behave karta hai , aur woh khud decide karta hai kab switch karna hai.
Template Method : ek recipe jiske steps fixed hain lekin tum filling choose karte ho.
Chain of Responsibility : tum ek sawaal poochte ho; agar pehle bacche ko nahi pata, woh agli ko pass karta hai, aur aise chalta hai.
Mediator : sab bacchon ke aapas mein chillane ki jagah, sab teacher se baat karte hain.
Memento : tum apni Lego build ki photo lete ho taaki agar toot jaaye toh exactly rebuild kar sako.
Visitor : ek guest aata hai aur ek kaam karta hai (khilaune ginta hai) har khilaune ke saath, khilauniyon ko change kiye bina.
Woh ek problem kya hai jo SARE behavioral patterns address karte hain? Objects kaise communicate karte hain aur runtime par responsibilities kaise divide hoti hain (control/messaging ka flow), ek patli contract ke zariye jo collaborators ko decouple karti hai.
Observer: subscriber list ka maalik kaun hai aur notify() kya karta hai? Subject list ka maalik hai; notify() observers par loop karta hai har observer ka update() ek interface ke through call karta hai — ek decoupled fan-out broadcast.
Strategy vs State — same UML, difference kya hai? Intent. Strategy objects independent hote hain aur client dwara choose kiye jaate hain; State objects ek doosre ko jaante hain aur context par khud transitions trigger karte hain.
Strategy vs Template Method? Strategy ek algorithm ko composition se vary karta hai (object pass in kiya); Template Method inheritance se (fixed skeleton mein hook steps override karo).
Command pattern ek ___ ko ___ mein convert karta hai, kaun se 3 cheezein enable karta hai? Ek request ko ek object mein; undo/redo, queuing/scheduling, aur logging enable karta hai.
Iterator ka key benefit kya hai? Collection ki internal representation expose kiye bina traverse karo; traversal state iterator mein hoti hai isliye multiple independent traversals possible hain.
Chain of Responsibility: ek handler jo request handle nahi kar sakta woh kya karta hai? Use apne next handler ko pass karta hai; chain wahan rukti hai jahan pehla handler succeed karta hai (ya end se gir jaata hai → unhandled).
Mediator vs Observer? Dono decouple karte hain, lekin Mediator ek hub mein specific coordination logic centralize karta hai (many-to-many → star); Observer generic one-to-many broadcast hai jisme koi coordination logic nahi.
Memento vs Command-based undo? Memento ek full state snapshot store karta hai (state replace karke restore); Command store karta hai kisi specific action ko reverse kaise karein . Memento tab use karo jab reversing mushkil ho lekin snapshotting sasti ho.
Visitor ka core mechanism aur trade-off kya hai? Double dispatch (element.accept(v) → v.visitX(element)). Naye operations add karna easy, naye element types add karna mushkil — normal OOP ka mirror.
Har behavioral pattern ke peechhe universal "trick" kya hai? Ek hard-coded direct call ya if/switch ko ek swappable/storable/queuable/notifiable object se replace karo.
Template Method kaun sa principle embody karta hai? Inversion of control / Hollywood Principle: "Don't call us, we'll call you" — base method subclass hooks ko ek fixed order mein call karta hai.
Design Patterns — Creational — woh objects banao jo behavioral patterns phir wire karte hain.
Design Patterns — Structural — objects compose karo; Behavioral define karta hai ki composed objects kaise interact karte hain .
SOLID Principles — Strategy/State/Command Open–Closed enact karte hain; Visitor aur Observer Dependency Inversion interfaces ke zariye enact karte hain.
Polymorphism — har behavioral pattern dynamic dispatch par lean karta hai.
Publish-Subscribe & Event-Driven Architecture — Observer processes/network par scale hota hai.
Undo-Redo Systems — Command + Memento saath kaam karte hain.
Swap object for if/switch
Communication and control flow