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 |

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#

- 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.

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.

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

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

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
hasNextandnextβ¦ 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<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#

| β 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#

- 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.

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 appropriateupdatefunction." β 10:29
2.3 Code walkthrough β 10:50#
Step 1 β the abstract Observer.

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

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

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

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

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#

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.

| 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#

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.

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

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#

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#

| β 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 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