Track A · LLD · Module A4 · Weeks 6–7

Behavioral
Patterns

12 patterns · 2 weeks · BookMyShow mini project
Strategy
Observer
Chain of Resp.
State
Command
Template Method
Iterator
Mediator
Memento
Visitor
Null Object
Interpreter

Behavioral patterns deal with algorithms and object communication — how responsibilities are distributed and how objects interact. Click any card to jump to its deep dive.

Week 6 — Core Six
01
Strategy
→ Payment System
Swap algorithms at runtime via interface
02
Observer
→ Stock Ticker
One-to-many auto notification on state change
03
Chain of Resp.
→ ATM Dispenser
Pass request along chain until handled
04
State
→ Vending Machine
Behaviour changes as internal state changes
05
Command
→ Smart Home
Encapsulate requests as objects. Enables undo.
06
Template Method
→ Data Migration
Fixed skeleton; defer steps to subclass
Week 7 — Supporting Six
07
Iterator
→ Custom Playlist
Traverse collection without exposing internals
08
Mediator
→ Air Traffic Control
Central hub decouples many-to-many peers
09
Memento
→ Text Editor Undo
Snapshot state for undo without breaking encapsulation
10
Visitor
→ Tax Calculator
New operations without modifying element classes
11
Null Object
→ Logger
Default no-op object eliminates null checks everywhere
12
Interpreter
→ Math Expr. Parser
Evaluate grammar-based expressions / DSL rules
PATTERNTRIGGER / SMELLKEY MECHANISMSOLID PRINCIPLE
Strategyif/else branching on algorithm typeInject strategy via interfaceOCP, DIP
ObserverOne change → notify many dependentsRegister/notify observersOCP, SRP
Chain of Resp.Multiple potential handlers, unknown upfrontHandlers form a linked chainSRP, OCP
Stateswitch/if on growing state fieldState objects hold behaviour; context delegatesSRP, OCP
CommandNeed undo/redo, queue, or logging of opsCommand objects with execute() + undo()SRP, OCP
Template MethodSame algorithm, different step implementationsAbstract class; subclasses override stepsOCP, DRY
IteratorNeed sequential access to collection internalsIterator object with hasNext/nextSRP
MediatorN objects all talk directly → N² connectionsAll communication routes through mediatorSRP, DIP
MementoNeed save/restore state without exposing internalsOriginator creates opaque Memento; Caretaker stores itEncapsulation
VisitorMany operations on stable class hierarchyaccept(visitor) → double dispatchOCP, SRP
Null ObjectNull checks scattered everywhereNo-op implementation of same interfaceSRP, LSP
InterpreterBuilding expression evaluator or DSLGrammar as class hierarchy; interpret(context)OCP
01Strategy
02Observer
03Chain
04State
05Command
06Template
07Iterator
08Mediator
09Memento
10Visitor
11Null Obj
12Interpret
01
Strategy
REAL SYSTEM → Payment System (UPI / Card / Wallet)
Define a family of algorithms, encapsulate each, make them interchangeable. Algorithm varies independently from the client. Eliminates if/else on algorithm type.
PaymentStrategy.javaJAVA
interface PaymentStrategy {
    boolean pay(double amount);
}
class UPIPayment implements PaymentStrategy {
    private final String upiId;
    public boolean pay(double amt) {
        System.out.println("Paid ₹"+amt+" via UPI: "+upiId); return true;
    }
}
class WalletPayment implements PaymentStrategy {
    private double balance;
    public boolean pay(double amt) {
        if (balance < amt) { System.out.println("Insufficient"); return false; }
        balance -= amt; return true;
    }
}

// Context — knows nothing about UPI/Wallet internals
class ShoppingCart {
    private PaymentStrategy strategy;

    public void setStrategy(PaymentStrategy s) { this.strategy = s; }

    public boolean checkout() {
        return strategy.pay(getTotal()); // Delegates — no if/else
    }
}

// Swap algorithm at runtime:
cart.setStrategy(new UPIPayment("ajay@icici"));  cart.checkout();
cart.setStrategy(new WalletPayment("PAYTM", 500)); cart.checkout();
Interview: Strategy is OCP in action. Every if/else on algorithm type is a Strategy violation. When asked "how do you add a new payment method without changing existing m4-code?" — this is the answer.
02
Observer
REAL SYSTEM → Stock Ticker / Event Bus
Define a one-to-many dependency: when subject changes state, all observers are notified automatically. Decouples publisher from subscribers — neither knows about the other's implementation.
StockTicker.javaJAVA
interface StockObserver {
    void update(String symbol, double price, double changePct);
}

class StockTicker {
    private final List<StockObserver> observers = new ArrayList<>();
    private final Map<String, Double> prices = new HashMap<>();

    public void subscribe(StockObserver o)   { observers.add(o); }
    public void unsubscribe(StockObserver o) { observers.remove(o); }

    public void updatePrice(String sym, double newPrice) {
        double old = prices.getOrDefault(sym, newPrice);
        double pct = ((newPrice - old) / old) * 100;
        prices.put(sym, newPrice);
        // PUSH model: send data directly to observers
        observers.forEach(o -> o.update(sym, newPrice, pct));
    }
}

// Concrete observers — totally independent of each other
class MobileApp implements StockObserver {
    public void update(String s, double p, double chg) {
        System.out.printf("[App] %s: ₹%.2f (%+.2f%%)%n", s, p, chg);
    }
}
class AlertService implements StockObserver {
    public void update(String s, double p, double chg) {
        if (Math.abs(chg) > 5.0) System.out.println("🚨 BIG MOVE: "+s);
    }
}

Push vs Pull

  • Push: subject sends data in update(). Simpler, observer always has data.
  • Pull: subject calls update(this). Observer fetches what it needs. More flexible.

Real-world Observers

  • → Java EventListener (ActionListener)
  • → Spring ApplicationEvent
  • → Kafka (producer → consumers)
  • → RxJava reactive streams
Interview: "Kafka is Observer at scale — stock ticker is the producer, topic is the subject, consumer groups are observers. The pattern scales from in-process EventBus to distributed Kafka."
03
Chain of Responsibility
REAL SYSTEM → ATM Cash Dispenser
Give multiple handlers a chance to process a request. Chain them; pass the request along until handled. Decouples sender from receiver — sender doesn't know which handler will process it.
ATMDispenser.javaJAVA
abstract class CashHandler {
    protected CashHandler next;

    // Fluent chaining: h1.setNext(h2).setNext(h3)
    public CashHandler setNext(CashHandler n) { this.next = n; return n; }
    public abstract void dispense(int amount);
}

class TwoThousandHandler extends CashHandler {
    public void dispense(int amount) {
        int notes = amount / 2000, rem = amount % 2000;
        if (notes > 0) System.out.println("Dispensing "+notes+"×₹2000");
        if (rem > 0 && next != null) next.dispense(rem); // Pass remainder
    }
}
class FiveHundredHandler extends CashHandler { /* similar */ }
class HundredHandler     extends CashHandler { /* similar */ }

// Build the chain
CashHandler atm = new TwoThousandHandler();
atm.setNext(new FiveHundredHandler())
   .setNext(new HundredHandler());

atm.dispense(3700);
// → Dispensing 1×₹2000 | 3×₹500 | 2×₹100
Interview: "Servlet Filters and Spring Interceptors are CoR — auth filter → logging filter → compression filter. Each handles its concern and passes to next. Order matters and is explicit."
04
State
REAL SYSTEM → Vending Machine
Allow an object to alter its behaviour when internal state changes. Eliminates switch-on-state. Each State class encapsulates one state's behaviour + valid transitions.
VendingMachineState.javaJAVA
interface VendingMachineState {
    void insertCoin(VendingMachine m, int amount);
    void selectProduct(VendingMachine m, String m4-code);
    void dispense(VendingMachine m);
    void cancel(VendingMachine m);
}

class IdleState implements VendingMachineState {
    public void insertCoin(VendingMachine m, int amt) {
        m.setAmount(amt);
        m.setState(new HasMoneyState()); // TRANSITION
    }
    public void selectProduct(VendingMachine m, String c) {
        System.out.println("Insert coin first");  // Invalid in this state
    }
    public void dispense(VendingMachine m)         { /* invalid */ }
    public void cancel(VendingMachine m)           { /* nothing to cancel */ }
}

class HasMoneyState implements VendingMachineState { /* ... */ }
class DispensingState implements VendingMachineState { /* ... */ }

// Context — delegates everything to current state
class VendingMachine {
    private VendingMachineState state = new IdleState();

    public void insertCoin(int amt) { state.insertCoin(this, amt); }
    public void setState(VendingMachineState s) { this.state = s; }
}
Interview: "State vs Strategy: State changes automatically based on internal transitions. Strategy is changed externally by the client. Both use the same polymorphism trick — the difference is who controls the switch and why."
05
Command
REAL SYSTEM → Smart Home + Undo Stack
Encapsulate a request as an object. Enables: undo/redo (store history), queuing (batch execution), logging (serialize commands), and macro commands (compose many as one).
SmartHomeCommand.javaJAVA
interface Command { void execute(); void undo(); }

class LightOnCommand implements Command {
    private final Light light;
    public void execute() { light.turnOn(); }
    public void undo()    { light.turnOff(); } // Inverse operation
}

class ACTempCommand implements Command {
    private final AC ac; private final int newTemp;
    private int prevTemp; // Saved for undo

    public void execute() { prevTemp = ac.getTemp(); ac.setTemp(newTemp); }
    public void undo()    { ac.setTemp(prevTemp); }
}

// Invoker — the smart home controller
class SmartHomeHub {
    private final Deque<Command> history = new ArrayDeque<>();

    public void execute(Command cmd) {
        cmd.execute(); history.push(cmd); // Push to undo stack
    }

    public void undo() {
        if (!history.isEmpty()) history.pop().undo();
    }
}

hub.execute(new LightOnCommand(bedroom));
hub.execute(new ACTempCommand(ac, 20));
hub.undo(); // AC reverts to previous temp
hub.undo(); // Light turns off
Interview: "Command is the pattern behind every undo stack — Photoshop, Word, VS Code all use it. Each user action is a Command object. Ctrl+Z pops the stack and calls undo(). Ctrl+Y pushes back and calls execute()."
06
Template Method
REAL SYSTEM → Data Migration Pipeline
Define the skeleton of an algorithm in a base class; defer specific steps to subclasses. The template method is final — skeleton never changes. Individual steps can be overridden (Hollywood Principle: "Don't call us, we'll call you").
DataMigrationPipeline.javaJAVA
abstract class DataMigrationPipeline {
    // TEMPLATE METHOD — final: order never changes
    public final void migrate() {
        extractData();    // abstract — must override
        validateData();   // hook    — may override
        transformData(); // abstract — must override
        loadData();       // abstract — must override
        notifyDone();     // hook    — default OK
    }
    protected abstract void extractData();
    protected abstract void transformData();
    protected abstract void loadData();
    protected void validateData() { System.out.println("Schema validation"); }
    protected void notifyDone()   { System.out.println("Migration done"); }
}

class MySQLToPostgres extends DataMigrationPipeline {
    protected void extractData()   { System.out.println("SELECT * FROM MySQL"); }
    protected void transformData() { System.out.println("ENUM→text, TINYINT→bool"); }
    protected void loadData()      { System.out.println("COPY INTO Postgres"); }
    protected void validateData()  { System.out.println("Row count + FK check"); }
}

new MySQLToPostgres().migrate(); // Runs steps in correct order — always
Strategy vs Template Method: Strategy swaps the WHOLE algorithm at runtime via composition. Template Method keeps the skeleton fixed in a base class; only specific steps vary via inheritance. Strategy = runtime; Template = compile-time.
07
Iterator
→ Custom Playlist / Collections
Traverse a collection sequentially without exposing its internal structure. Client only uses hasNext() / next() — doesn't care if internals are List, Tree, Graph, or LinkedList.
PlaylistIterator.javaJAVA
interface Iterator<T> { boolean hasNext(); T next(); }

class Playlist {
    private final List<Song> songs = new ArrayList<>();

    public Iterator<Song> createIterator() {
        return new Iterator<Song>() {
            int i = 0;
            public boolean hasNext() { return i < songs.size(); }
            public Song next()      { return songs.get(i++); }
        };
    }
}
// Client — doesn't know internals are a List
Iterator<Song> it = playlist.createIterator();
while (it.hasNext()) play(it.next());
Interview: "Java's for-each loop uses Iterator internally. Any class implementing java.lang.Iterable gets free for-each support. This is Iterator pattern baked into the language."
08
Mediator
→ Air Traffic Control
Encapsulate how a set of objects interact. Objects don't refer to each other — they all communicate through the mediator. Reduces N² direct connections to N connections via hub.
ATCTower.javaJAVA
interface ATC {
    void requestLanding(Aircraft a);
    void requestTakeoff(Aircraft a);
    void broadcast(String msg, Aircraft source);
}

abstract class Aircraft {
    protected final ATC atc; // Only knows ATC — not other aircraft
    public void land()    { atc.requestLanding(this); }
    public void takeoff() { atc.requestTakeoff(this); }
    public abstract void receive(String msg);
}

// Aircraft talk ONLY to ATC tower — never directly to each other
// ATC routes communication, manages runway, notifies all parties
Mediator vs Observer: Observer = one-to-many (subject notifies all). Mediator = many-to-many (all peers route through hub). Chat room is Mediator — messages go hub → recipients, not sender → every recipient directly.
09
Memento
→ Text Editor Undo / Game Save
Capture object state without exposing internals. Originator creates memento; Caretaker stores it; Originator restores from it. Three-role pattern preserving encapsulation.
TextEditorUndo.javaJAVA
// MEMENTO — opaque snapshot (Caretaker can't read it)
class EditorMemento {
    private final String m4-content;  // package-private — only Originator reads
    private final int    cursor;
    EditorMemento(String c, int pos) { m4-content=c; cursor=pos; }
    String getContent() { return m4-content; }
    int    getCursor()  { return cursor; }
}

// ORIGINATOR — creates and restores from memento
class TextEditor {
    private StringBuilder m4-content = new StringBuilder();
    private int cursor = 0;

    public EditorMemento save()          { return new EditorMemento(m4-content.toString(), cursor); }
    public void restore(EditorMemento m) { m4-content = new StringBuilder(m.getContent()); cursor=m.getCursor(); }
}

// CARETAKER — stores mementos, never reads inside them
class UndoManager {
    private final Deque<EditorMemento> stack = new ArrayDeque<>();
    public void           push(EditorMemento m) { stack.push(m); }
    public EditorMemento pop()                  { return stack.pop(); }
}
Interview: "Three roles: Originator (knows its own state), Memento (opaque snapshot), Caretaker (stores mementos, never reads them). Encapsulation is preserved because only Originator can interpret the Memento's internals."
10
Visitor
→ Tax Calculator (GST + Import Duty)
Add new operations to elements without changing their classes. Double dispatch: element.accept(visitor) → visitor.visit(element). New operations = new Visitor; no changes to Book/Electronics/Food.
TaxVisitor.java — Double DispatchJAVA
interface TaxVisitor {
    double visit(Book b);
    double visit(Electronics e);
    double visit(Food f);
}
interface Product { double accept(TaxVisitor v); } // DOUBLE DISPATCH key

class Book implements Product {
    boolean educational;
    public double accept(TaxVisitor v) { return v.visit(this); } // Passes self
}

class GSTCalculator implements TaxVisitor {
    public double visit(Book b)        { return b.educational ? 0 : b.price*0.12; }
    public double visit(Electronics e) { return e.price * 0.18; }
    public double visit(Food f)        { return f.processed ? f.price*0.12 : 0; }
}
// Add ImportDutyCalculator = new class only — Book/Electronics/Food untouched (OCP)
Interview: "Double dispatch is the key. In Java, method dispatch is on the runtime type of ONE argument. Visitor simulates two-argument dispatch: element type × visitor type. accept() provides the second dispatch."
11
Null Object
→ Logger / Optional Dependencies
Provide a default do-nothing implementation to avoid null checks. Client m4-code never checks for null — calls methods on whatever was injected, safely.
NullLogger.javaJAVA
interface Logger { void log(String msg); void error(String msg); }

class ConsoleLogger implements Logger {
    public void log(String msg)   { System.out.println("[LOG] "+msg); }
    public void error(String msg) { System.err.println("[ERR] "+msg); }
}

// NULL OBJECT — same interface, does nothing
class NullLogger implements Logger {
    public void log(String msg)   { /* no-op */ }
    public void error(String msg) { /* no-op */ }
}

class PaymentService {
    private final Logger log;
    public PaymentService(Logger log) {
        this.log = log != null ? log : new NullLogger(); // Never null after this
    }
    public void process(double amt) {
        log.log("Processing ₹"+amt); // Safe — always. No null check.
    }
}
Interview: "Null Object is the pattern behind Optional in Java 8+ — instead of checking isPresent(), you call ifPresent() which is a no-op when empty. Eliminates NullPointerException from forgotten null checks."
12
Interpreter
→ Math Expression Parser / Rules Engine
Represent a grammar as a class hierarchy. Each grammar rule is a class. Compose terminal and non-terminal expressions into an AST. Call interpret(context) to evaluate.
ExpressionParser.javaJAVA
interface Expression { int interpret(Map<String,Integer> ctx); }

// TERMINAL — leaves of the AST
class NumberExpr   implements Expression {
    private final int n;
    public int interpret(Map ctx) { return n; }
}
class VariableExpr implements Expression {
    private final String name;
    public int interpret(Map ctx) { return (int) ctx.getOrDefault(name, 0); }
}

// NON-TERMINAL — composite nodes
class AddExpr implements Expression {
    private final Expression l, r;
    public int interpret(Map ctx) { return l.interpret(ctx) + r.interpret(ctx); }
}

// Parse "a + b * 3" → a + (b * 3)
Expression expr = new AddExpr(
    new VariableExpr("a"),
    new MultiplyExpr(new VariableExpr("b"), new NumberExpr(3)));
expr.interpret(Map.of("a", 5, "b", 4)); // → 17
Interview: "Interpreter works well for simple grammars — math expressions, SQL WHERE parsers, firewall rules engines. For complex grammars, use a parser generator (ANTLR) instead. Composite pattern is Interpreter's structural cousin."
Strategy vs State vs Template Method
Strategy — Swap the whole algorithm at runtime. Changed externally by client. Uses composition.

State — Algorithm changes automatically as internal state transitions. States know about each other. Self-transitions.

Template Method — Algorithm skeleton fixed in base class. Only specific steps vary via inheritance. Compile-time decision.

Rule: Who controls the switch? Client→Strategy. Object→State. Compiler→Template.
Observer vs Mediator vs CoR
Observer — 1-to-many: subject broadcasts to all registered observers. Observers don't know each other.

Mediator — Many-to-many: all peers talk through central hub. Hub coordinates responses. Peers know Mediator, not each other.

Chain of Responsibility — Request travels down a chain. Each handler decides to handle or pass. No hub — linear.

Rule: All notified? Observer. Hub decides routing? Mediator. Linear pass-through? CoR.
Command vs Strategy vs Template
Command — Encapsulates a REQUEST (with undo, queue, log). About WHO initiated the action and when.

Strategy — Encapsulates an ALGORITHM (interchangeable). About HOW the action is performed.

Rule: Need undo/queue/log → Command. Need interchangeable algorithm → Strategy. Both look similar — the intent distinguishes them.
Memento vs Command (for undo)
Memento undo — Saves entire state snapshot. Easy to implement. Heavy (stores full state).

Command undo — Stores inverse operations. Lightweight (stores only what changed). More complex.

When to choose: State changes are small and known → Command undo. State is complex or external → Memento snapshot.

Most real editors use Command undo for efficiency.
// QUICK ONE-LINE TEST FOR EACH PATTERN
Strategy: "Which algorithm should I use right now?"
Observer: "Notify everyone when this changes"
CoR: "Try each handler until one succeeds"
State: "I behave differently depending on mode"
Command: "Record this action so I can undo it"
Template: "Same steps, different implementations"
Iterator: "Walk through this without knowing how it's stored"
Mediator: "Route all messages through a hub"
Memento: "Take a snapshot I can restore later"
Visitor: "Add operations without touching element classes"
Null Obj: "Do nothing instead of crashing on null"
Interpret:"Evaluate this expression in my mini-language"
// STATE PATTERN IN ACTION
Seat State Machine — BookMyShow

The seat lifecycle in BookMyShow is a perfect State pattern example. Each state defines which transitions are valid and which are illegal.

AVAILABLE
tryLock(userId)
5s timeout
LOCKED
confirmPayment()
BOOKED
cancel()
CANCELLED
AVAILABLE
✓ tryLock(userId) → LOCKED
✗ confirmPayment() → error
✗ cancel() → error
LOCKED
✓ confirmPayment() → BOOKED
✓ timeout() → AVAILABLE
✓ cancel() → AVAILABLE
✗ tryLock() by others → error
BOOKED
✓ cancel() → CANCELLED
✗ tryLock() → error
✗ confirmPayment() → error
CANCELLED
✓ relist() → AVAILABLE
✗ all others → error
(terminal state)
// CONCURRENCY-SAFE SEAT LOCKING
Seat.java — Thread-safe State + LockJAVA
class Seat {
    private volatile SeatState state = SeatState.AVAILABLE;
    private final ReentrantLock lock = new ReentrantLock();
    private String lockedBy;

    public boolean tryLock(String userId, long timeoutMs) {
        try {
            if (lock.tryLock(timeoutMs, TimeUnit.MILLISECONDS)) {
                if (state == SeatState.AVAILABLE) {
                    state    = SeatState.LOCKED;
                    lockedBy = userId;
                    scheduleLockExpiry(5_000); // Auto-release after 5s
                    return true;
                }
                lock.unlock(); // Not available — release lock
            }
        } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
        return false; // Seat taken or timeout
    }
}
MINI PROJECT
BookMyShow — LLD + Concurrency

The capstone project for Module A4. Uses 6 behavioral patterns woven together with real concurrency handling. This is the most complex LLD problem so far.

🎭
Seat State Machine
State Pattern
AVAILABLE → LOCKED → BOOKED → CANCELLED. Each state defines valid transitions. ReentrantLock ensures thread-safe transitions.
🔔
Booking Notifications
Observer Pattern
BookingService is Subject. User's email, SMS, push are observers. Notified on confirmation, cancellation, reminders.
↩️
Book / Cancel Actions
Command Pattern
BookSeatCommand + CancelBookingCommand with undo(). Stack enables cancellation with refund in reverse order of seats booked.
💰
Ticket Pricing
Strategy Pattern
WeekendPricingStrategy, WeekdayPricingStrategy, HolidayPricingStrategy — injected at show creation. Swap without touching BookingService.
🔗
Booking Pipeline
Chain of Responsibility
SeatAvailabilityHandler → PaymentHandler → BookingConfirmationHandler → NotificationHandler. Each step passes to next or aborts.
🏛️
BookingFacade
Facade (from A3)
bookSeats(userId, showId, seatIds) hides: seat locking, payment, ticket generation, observer notification, audit logging.
// PROJECT STRUCTURE
bookmyshow/
├── model/ Movie, Show, Screen, Seat, User, Booking, Ticket
├── state/ SeatState enum + state transition logic
├── command/ BookSeatCommand, CancelBookingCommand
├── observer/ BookingObserver, EmailNotifier, SMSNotifier, PushNotifier
├── strategy/ PricingStrategy, WeekendPricing, WeekdayPricing
├── chain/ BookingHandler chain: Availability → Payment → Confirm → Notify
├── service/ BookingService, PaymentService, NotificationService
├── facade/ BookingFacade — single public entry point
└── BookMyShowDemo.java 5 concurrent users, 1 show, race condition demo
01
Pattern Recognition — 6 Scenarios
~1.5 hrs

Identify the correct Behavioral pattern. 2-sentence justification each.

1. Text editor needs Ctrl+Z undo for bold, italic, insert, delete.
2. Social media notifies followers when user posts. Follower list changes dynamically.
3. Loan application: Credit Check → Income Verify → Background → Approval.
4. Traffic light cycles RED → GREEN → YELLOW. Valid actions differ per phase.
5. Zip utility supports DEFLATE/BZIP2/LZMA, switchable per file type at runtime.
6. Shopping cart: calculate total price, weight, customs duty as separate passes
   without adding methods to Product classes.
02
Thread-safe EventBus (Observer)
~2.5 hrs · m4-code

Build a generic publish/subscribe EventBus with thread safety.

API:
  subscribe(Class<T> eventType, Consumer<T> handler)
  unsubscribe(Class<T> eventType, Consumer<T> handler)
  publish(T event)

Events: OrderPlaced, PaymentFailed, ItemShipped

Requirements:
- Multiple handlers per event type
- Thread-safe: concurrent publish() + subscribe() calls
- Handlers run asynchronously (use ExecutorService)
- Failed handler must not block other handlers
- Unsubscribe mid-flight must not cause ConcurrentModificationException
03
ReportGenerator — Template Method + Strategy
~2 hrs · m4-code

Combine Template Method for pipeline structure with Strategy for delivery.

Template Method skeleton (in abstract base):
  gatherData() → processData() → formatOutput() → deliver()

Subclasses override formatOutput():
  HTMLReportGenerator → formatOutput() returns HTML string
  PDFReportGenerator  → formatOutput() returns byte[]

Strategy for deliver() (injected, runtime-swappable):
  EmailDelivery   — sends via SMTP
  SlackDelivery   — sends via Slack webhook

Show all 4 combinations work:
  new HTMLReportGenerator(new EmailDelivery()).generate()
  new HTMLReportGenerator(new SlackDelivery()).generate()
  new PDFReportGenerator(new EmailDelivery()).generate()
  new PDFReportGenerator(new SlackDelivery()).generate()
BookMyShow — Full LLD + Concurrency
~6 hrs · full project

Complete LLD implementation. The concurrency handling is the critical differentiator.

Implement all 6 pattern usages:
  State:   Seat state machine (AVAILABLE→LOCKED→BOOKED→CANCELLED)
  Observer: Booking confirmation/cancellation notifications
  Command: BookSeatCommand + CancelBookingCommand with undo()
  Strategy: Pricing (Weekend 1.5x, Holiday 2x, Weekday 1.0x)
  CoR:     Availability→Payment→Confirm→Notify handler chain
  Facade:  BookingFacade.bookSeats(userId, showId, seatIds)

Demo: 5 threads simultaneously try to book the last 2 seats
  → Only 2 succeed, 3 get "seat unavailable"
  → No double booking under any timing

Deliverable:
  1. Full Java implementation (all classes)
  2. Concurrency test showing thread-safe behaviour
  3. UML class diagram with all 6 patterns annotated
0 / 12 completedA4 → Behavioral Patterns
Can implement Strategy, Observer, CoR, State, Command from memory
Can implement Template Method, Iterator, Mediator, Memento, Visitor from memory
Know Null Object and Interpreter well enough to apply and explain
Can distinguish Strategy vs State vs Template Method with the one-line test
Know Observer push vs pull model trade-offs
Understand Command's role in undo/redo, queue, and macro commands
Understand Visitor's double dispatch mechanism
Know Memento's 3-role structure (Originator, Memento, Caretaker)
✏️ Task 1: 6 pattern recognition scenarios answered correctly
✏️ Task 2: Thread-safe EventBus with async handlers implemented
✏️ Task 3: ReportGenerator combining Template Method + Strategy (4 combos)
✏️ Mini Project: BookMyShow with concurrency proof + all 6 patterns + UML
NEXT MODULE
A5 — Concurrency in LLD
Thread safety, locks, semaphores, producer-consumer, thread pools, deadlock avoidance. Projects: Thread-safe Parking Lot, Rate Limiter, Pub/Sub Message Queue.
← PREVIOUS: LLD A3 📄 READ STUDY NOTES ↑ ROADMAP NEXT: LLD A5 →