W8_L6 β€” Design Patterns: Behavioral#

Video W8_L6: Design Patterns-Behavioral
URL https://youtu.be/immIH_tlfkI
Channel IIT Madras β€” B.S. Degree Programme (Software Engineering)
Duration 24:50

What this lecture covers: three behavioral design patterns β€” Iterator, Observer, and Strategy β€” each presented as problem β†’ solution/class diagram β†’ Java code β†’ pros and cons, followed by a wrap-up of the whole design-patterns unit.


0. Where behavioral patterns fit β€” 00:26#

The previous videos covered two of the three families. This one closes the set.

Family What it governs
Creational Used during the process of object creation
Structural Composition of classes or objects β€” how to assemble objects and classes into larger structures
Behavioral "Characterize the ways in which classes or objects interact and distribute responsibility" β€” effective communication + assignment of responsibilities between objects

Types of design patterns
Types of design patterns

The distinguishing question for a behavioral pattern is not "how do I build this object?" or "how do I structure these classes?" but "who talks to whom, and who is responsible for what?"


1. Iterator Design Pattern#

1.1 The problem β€” 01:20#

Iterator problem
Iterator problem

  • We use many different collection types β€” lists, queues, stacks, trees.
  • A universally common operation is to access and iterate through the elements: a list of products, a list of orders, a list of deliveries.
  • But: the client is concerned only with accessing elements, not with how the elements are stored.

"As a client I am concerned only with accessing these elements. I am not really interested in how these elements are stored or what is the underlying data structure which stores these collections." β€” 02:24

So traversal logic must not leak into client code, and the client must not be rewritten every time the underlying data structure changes.

1.2 The solution β€” 02:41#

Separate the behaviour of how elements are accessed into a separate object called an iterator.

Iterator solution and Java interface
Iterator solution and Java interface

Java already ships this as the Iterator interface, with two key methods:

Method Returns
hasNext() true if the iteration has more elements
next() the next element in the iteration

Concrete collection classes implement this interface, and the implementation details of how to iterate live inside those methods β€” not in the client.

1.3 Code walkthrough β€” 03:37#

Step 1 β€” a plain domain class. A Book class with fields isbn, title, cost, author, published, description, plus getters.

Book class
Book class

Step 2 β€” a constructor and a collection. Three books b1, b2, b3 are created and added to an ArrayList<Book>.

Book constructor and ArrayList
Book constructor and ArrayList

ArrayList<Book> bookList = new ArrayList<Book>();
Book b1 = new Book(isbn:"1", title:"Book1", cost:100, author:"Author1", Year.of(isoYear:2002), description:"Book 1 Description");
Book b2 = new Book(isbn:"2", title:"Book2", cost:100, author:"Author1", Year.of(isoYear:2002), description:"Book 2 Description");
Book b3 = new Book(isbn:"3", title:"Book3", cost:100, author:"Author1", Year.of(isoYear:2002), description:"Book 3 Description");

bookList.add(b1);
bookList.add(b2);
bookList.add(b3);

Step 3 β€” get an iterator and traverse. 04:19

ArrayList iterator with while loop
ArrayList iterator with while loop

Iterator<Book> bookIterator = bookList.iterator();

while (bookIterator.hasNext()) {
    System.out.println(bookIterator.next().getTitle());
}

ArrayList supplies the hasNext() and next() implementations.

"As a client I need not know what are the implementation details of hasNext and next … because that has been delegated to the specific collection which we are calling." β€” 04:56

Step 4 β€” swap the data structure, keep the traversal code. 05:47

LinkedList iterator
LinkedList iterator

LinkedList<Book> bookList2 = new LinkedList<Book>();
bookList2.add(b1);
bookList2.add(b2);

bookIterator = bookList2.iterator();   // same client-side traversal code works

LinkedList also has its own implementations of hasNext() and next(). This is the payoff: the traversal code is unchanged even though the storage went from array-backed to node-backed.

1.4 Pros and cons β€” 06:24#

Iterator pros and cons
Iterator pros and cons

βœ… Single Responsibility Principle Separates access of elements from other functionalities β€” accessing is delegated to a separate class, the iterator
βœ… Open/Closed Principle You can implement new types of iterators and new types of collections; extend or implement the Iterator interface, or write your own, without touching existing code
❌ Overkill for simple collections For a simple structure like a plain array in a simple application, creating a separate iterator class is not worth it

2. Observer Design Pattern#

2.1 The problem β€” 07:55#

Observer problem
Observer problem

  • Feature wanted: notify a particular set of buyers when a new product is launched.
  • Familiar analogy 08:26: you subscribe to a YouTube channel; the channel posts a new video; if you subscribed to notifications, you get one.
  • The constraint that makes it interesting: "I do not want to send this notification to all users of my system, but only to those who have subscribed to get notifications." β€” 08:53

So: subscribers get a notification when there are updates β€” and only subscribers.

2.2 The solution β€” 09:13#

  • The subject object maintains a list of observers.
  • As and when updates happen, the subject notifies all the observers of any updates or changes.

Observer class diagram
Observer class diagram

Reading the class diagram:

Participant Responsibility
Subject Holds observerCollection; can registerObserver(), unregisterObserver(), and notifyObservers()
Observer (interface) Declares update()
ConcreteObserverA / ConcreteObserverB Implement update() with their own behaviour

And the mechanism, spelled out in the diagram's note:

notifyObservers()
  for observer in observerCollection
    call observer.update()

notifyObservers "simply iterates over the collection and calls the appropriate update function." β€” 10:29

2.3 Code walkthrough β€” 10:50#

Step 1 β€” the abstract Observer.

Abstract Observer class
Abstract Observer class

abstract class Observer {
    String observerName;
    public String getObserverName() { return observerName; }
    public void setObserverName(String observerName) { this.observerName = observerName; }

    public abstract void update(String productName);
}

Step 2 β€” two concrete observers with different behaviour. 11:03

NormalUser and PrimeUser
NormalUser and PrimeUser

class NormalUser extends Observer {
    public NormalUser(String name) { observerName = name; }

    @Override
    public void update(String productName) {
        System.out.println("Message sent to Normal User " + observerName);
        System.out.println("A new product " + productName + " has been released");
    }
}

class PrimeUser extends Observer {
    public PrimeUser(String name) { observerName = name; }

    @Override
    public void update(String productName) {
        System.out.println("Message sent to Super User " + observerName);
        System.out.println("A new product " + productName + " has been released");
        System.out.println("You are eligible for 20% discount");   // extra, prime-only line
    }
}

Same trigger, different message per observer type β€” the prime user additionally gets the discount line. Different types of observers/users can be created this way.

Step 3 β€” the Subject. 11:54

Subject class
Subject class

class Subject {
    private List<Observer> observerCollection = new ArrayList<>();

    public void registerObserver(Observer observer) {
        observerCollection.add(observer);
        System.out.println(observer.getObserverName() + " has been registered");
    }

    public void notifyObservers(String productName) {
        for (Observer observer : observerCollection) {
            observer.update(productName);
        }
    }
}

Step 4 β€” the client. 12:29

Observer client code
Observer client code

public class ObserverDesignPattern {
    public static void main(String[] args) {
        Subject subject = new Subject();

        Observer u1 = new NormalUser(name:"Normal User 1");
        Observer u2 = new PrimeUser(name:"Prime User 1");

        subject.registerObserver(u1);
        subject.registerObserver(u2);

        subject.notifyObservers(productName:"Apple iPhone 14");
    }
}

The client only calls notifyObservers once. The appropriate update for each observer is dispatched automatically β€” NormalUser.update() for u1, PrimeUser.update() for u2. Add a new user type and the right update gets called for it too, with no change to Subject or the client's notify call.

Step 5 β€” the output. 13:51

Observer output
Observer output

Normal User 1 has been registered
Prime User 1 has been registered
Message sent to Normal User Normal User 1
A new product Apple iPhone 14 has been released
Message sent to Super User Prime User 1
A new product Apple iPhone 14 has been released
You are eligible for 20% discount

The message sent to the normal user is different from the one sent to the prime user β€” from a single notifyObservers call.


3. Strategy Design Pattern#

3.1 The problem β€” 14:13#

Strategy problem
Strategy problem

Feature wanted: process shopping orders based on different strategies.

Strategy Rule
First in first out (FIFO) Orders that come first are processed first
Priority (based on type of user) Prime/super users get higher priority, so their orders are processed first; normal users' orders are processed later

3.2 The solution β€” 15:06#

  • Extract different strategies (algorithms) into separate classes.
  • The original class β€” called the context β€” delegates the work of implementing the algorithm to the strategy object.

Strategy class diagram
Strategy class diagram

Participant Role
Context The original class; holds a strategy and delegates to it
Strategy (interface) Declares execute() β€” the algorithm's entry point
ConcreteStrategyA / ConcreteStrategyB Each implements execute() with one specific algorithm

The algorithm lives in execute(); the context never contains it.

3.3 Before the pattern β€” and why it's a problem β€” 16:29#

Before strategy β€” OCP violation
Before strategy β€” OCP violation

class Order {
    // productName, priority, placeOrder() ...
}

class OrderManager {
    private ArrayList<Order> orderList = new ArrayList<>();

    public void addOrder(Order o) {
        orderList.add(o);
    }

    public void deliverOrderFIFO() {              // strategy #1 baked into the class
        for (Order o : orderList) {
            o.placeOrder();
        }
    }

    public void deliverOrderPriority() {          // strategy #2 baked into the class
        // logic for sorting orders by priority
        // send in that order
        System.out.println("Delivering orders based on priority");
    }

    // another method which implements the new strategy   ← every new strategy forces an edit here
}

Client:

OrderManager om = new OrderManager();
om.addOrder(o1);
om.addOrder(o2);
om.deliverOrderFIFO();

Why this is bad 17:37: to add another strategy you must add another method inside OrderManager.

"This in a way violates the open/closed principle, which states that objects or classes should be open to extension but closed for modification. In this case we are actually modifying this class β€” and that may not be desirable in most cases." β€” 18:00

3.4 After the pattern β€” 18:23#

Order is untouched. From OrderManager we remove the strategy-specific methods, and introduce an interface plus one class per strategy.

After strategy β€” interface and concrete strategies
After strategy β€” interface and concrete strategies

interface OrderDeliveryStrategy {
    public void deliverOrders(ArrayList<Order> orderList);
}

class FIFOOrderDelivery implements OrderDeliveryStrategy {
    @Override
    public void deliverOrders(ArrayList<Order> orderList) {
        for (Order o : orderList) {
            o.placeOrder();
        }
    }
}

class PriorityOrderDelivery implements OrderDeliveryStrategy {
    @Override
    public void deliverOrders(ArrayList<Order> orderList) {
        System.out.println("Delivering orders based on priority");
    }
}

Whatever logic was in deliverOrderFIFO() is now the body of FIFOOrderDelivery.deliverOrders(); the priority logic likewise moves into PriorityOrderDelivery.

The client now picks and swaps a strategy object. 20:04

Client: before vs after
Client: before vs after

public class StrategyPatternAfter {
    public static void main(String[] args) {
        Order o1 = new Order(productName:"Product 1", priority:1);
        Order o2 = new Order(productName:"Product 2", priority:2);

        OrderManager om = new OrderManager();
        om.addOrder(o1);
        om.addOrder(o2);

        OrderDeliveryStrategy strategy = new FIFOOrderDelivery();
        strategy.deliverOrders(om.getOrderList());

        strategy = new PriorityOrderDelivery();      // switch strategy at runtime
        strategy.deliverOrders(om.getOrderList());
    }
}

Note the contrast in the screenshot: the before client calls om.deliverOrderFIFO() β€” a method fixed on the manager; the after client assigns a strategy object and can change it later for some other condition with one line.

3.5 Adding a brand-new strategy costs zero edits β€” 20:52#

New strategy added without touching existing classes
New strategy added without touching existing classes

class NewStrategy implements OrderDeliveryStrategy {
    @Override
    public void deliverOrders(ArrayList<Order> orderList) {
        // TODO Auto-generated method stub
    }
}
strategy = new NewStrategy();
strategy.deliverOrders(om.getOrderList());

"Later on if you want to implement a new strategy you need not make changes in any of your code, in any of the existing classes. All that you can do is create a new class … and that will implement the order delivery strategy." β€” 20:52

3.6 Pros and cons β€” 21:50#

Strategy pros and cons
Strategy pros and cons

βœ… Isolates implementation details of the algorithm Each class holds the logic for one specific strategy
βœ… Open/Closed Principle Introduce new strategies without changing existing classes or methods
❌ Not required for few algorithms If the application has only one or two strategies, implementing the pattern isn't worth it

4. Unit summary β€” 22:59#

Design patterns summary
Design patterns summary

  • Design patterns are templates / descriptions of how objects and classes should be arranged and organized "to solve a general design problem in a particular context."

  • Three families: Creational, Structural, Behavioural β€” with examples of each covered across the videos.

  • There are around 23 such design patterns; this course does not cover all of them.

  • Value: when you encounter a problem, you can use one of these patterns to create more extendable and understandable code.

  • Caution β€” patterns are not to be used blindly 24:15:

    "The existence of a design pattern does not mean that we can use a design pattern in any context. A design pattern should not be used blindly, and it should be used only if you see that there is a benefit in using that design pattern." β€” 24:27

    Always weigh both the benefits and the drawbacks for your specific case.


5. Cheat sheet#

The three patterns at a glance#

Iterator Observer Strategy
Question it answers How do I traverse a collection without knowing its storage? How do I notify only interested parties when something changes? How do I swap the algorithm without editing the class that uses it?
What gets extracted Traversal / access behaviour The reaction to a change The algorithm
Key abstraction Iterator interface Observer (abstract/interface) + Subject Strategy interface
Key methods hasNext(), next() update(), registerObserver(), notifyObservers() execute() / deliverOrders()
Who holds the collection The collection (ArrayList, LinkedList) Subject holds observerCollection Context holds the order list
Lecture example Book in ArrayList vs LinkedList New product β†’ NormalUser vs PrimeUser Order delivery: FIFO vs Priority
Principle earned SRP + OCP OCP (new observer types) OCP
Skip it when Collection is simple (a plain array) β€” Only one or two algorithms exist

Observer vs Strategy β€” the easily-confused pair#

Both put an interface between a "main" class and a set of pluggable classes, so the class diagrams look alike. The difference is direction and cardinality:

Observer Strategy
How many pluggable objects are active at once Many β€” the subject holds a whole collection and calls all of them One β€” the context uses a single chosen strategy at a time
Purpose of the call Notification β€” "something happened, react" Delegation β€” "do this job for me"
Who initiates An event/update on the subject The client picking an algorithm
Direction One-to-many broadcast, subject β†’ observers One-to-one hand-off, context β†’ strategy

Notation key for the class diagrams#

Symbol Meaning
Β«interfaceΒ» Interface, not a concrete class
+methodName() Public member
Hollow triangle arrow ◁—— Implements / extends (the concrete observers β†’ Observer, concrete strategies β†’ Strategy)
Filled diamond β—†β€”β€” Composition (the Context owns its Strategy; Subject owns its observer collection)

Principles referenced in this lecture#

Principle Where it showed up
Single Responsibility Principle Iterator: element access is separated from the collection's other functionality
Open/Closed Principle β€” open to extension, closed for modification All three: new iterators/collections, new observer types, new strategies added without editing existing classes. The "before" Strategy code is the counter-example β€” it forces you to modify OrderManager.

6. File manifest#

w8-l6-design-patterns-behavioral/
β”œβ”€β”€ NOTES.md            # this file
β”œβ”€β”€ shots/              # 23 verified slide/code screenshots
β”œβ”€β”€ transcript_ts.txt   # timestamped transcript (471 lines)
β”œβ”€β”€ video.en.vtt        # raw YouTube auto-captions
└── video.mp4           # source video, 360p (36.7 MB)

Commands used#

export PATH="$HOME/.local/bin:$PATH"

# metadata
yt-dlp --skip-download \
  --print "%(title)s | %(duration_string)s | %(uploader)s | %(upload_date)s" \
  "https://youtu.be/immIH_tlfkI"

# subtitles
yt-dlp --skip-download --write-auto-subs --write-subs \
  --sub-lang en --sub-format vtt -o "video.%(ext)s" "https://youtu.be/immIH_tlfkI"
python3 ~/.claude/skills/youtube-notes/scripts/vtt2txt.py video.en.vtt > transcript_ts.txt

# video β€” 720p/1080p returned HTTP 403 on the default player client;
# alternate clients + combined format 18 (360p) worked
yt-dlp --extractor-args "youtube:player_client=tv,web_safari,ios" \
  -f 18 -o "video.%(ext)s" "https://youtu.be/immIH_tlfkI"

# frames
~/.claude/skills/youtube-notes/scripts/grab_frames.sh video.mp4 shots <<'EOF'
00:01:05|01_pattern_types
00:02:40|02_iterator_problem
...
EOF

# re-extraction of frames caught mid-animation / mid-scroll
ffmpeg -ss 00:12:10 -i video.mp4 -frames:v 1 -q:v 2 out.jpg -y