CH.07 · BEHAVIORAL PATTERNS

Software Design & Architecture · Behavioral Patterns

Iterator & Observer

Structural patterns decided how objects are put together. Behavioral patterns decide how they talk to each other — who walks a collection, and who gets told when something changes.

Chapter 07 · Lecture 02 Category Behavioral Design Patterns Previously Structural Patterns Next up Architecture & Design Evaluation
00

What Are Behavioral Patterns?

Behavioral patterns are responsible for the efficient and safe distribution of behaviors among a program's objects — how responsibility and communication are shared out at run time.

Mnemonic — I.O. → "Iterate, Observe" — Iterator hands out a standard way to walk a collection; Observer hands out a standard way to watch an object for changes. Walking and watching: the two behaviors this lecture covers.

Iterator

Gives clients one standard way to step through a collection — without ever revealing how that collection stores its elements internally.

Observer

Sets up a one-to-many link so that when one object changes state, everything depending on it is notified and updated automatically.

The rest of the family (named, not covered)

The lecture lists these so you recognise them as behavioral if they appear in a question: Command, Chain of Responsibility, Interpreter, Mediator, Memento, State, Strategy, Template Method and Visitor. Only Iterator and Observer are examinable in detail here.

01

The Iterator Pattern

Object Behavioral Pattern

Provides a standardized way of accessing and traversing the objects held in a collection data structure. Intent: provide a way to access the elements of a collection object sequentially, without any need to know its underlying representation.

Mnemonic — think of a TV remote: pressing channel up moves you to the next channel, and you never learn whether the channels are stored in an array, a list, or a database. The remote is the Iterator; the TV's channel store is the Aggregate.

The two words that matter

  • Sequentially — one element after another, in a defined order.
  • Without knowing the representation — the client never learns whether it is walking an array, a linked list, or a tree. Change the storage, and no client breaks.

Structure — who refers to whom

Client
Aggregate
+createIterator()
Aggregate1
+createIterator()
creates ⤳
accesses ⤳
Iterator
+next()
+hasNext()
Iterator1
+next()
+hasNext()
READING THE DIAGRAM — the Client refers to (1) the Aggregate interface to create an Iterator object via createIterator(), and (2) the Iterator interface to traverse the aggregate via next() and hasNext(). Iterator1 implements the Iterator interface by accessing Aggregate1. Note the client talks to interfaces on both sides — that is what hides the representation.

Worked Example — a name collection

Setup: a NameRepository stores names in a plain array, but the client should be able to walk it without knowing that. The concrete class implements Container and holds an inner class NameIterator that implements Iterator.

// The Iterator interface — the traversal contract
public interface Iterator {
  public boolean hasNext();
  public Object next();
}

// The Aggregate / Container interface — the "give me an iterator" contract
public interface Container {
  public Iterator getIterator();
}

// Concrete collection + its concrete iterator as an inner class
public class NameRepository implements Container {
  public String[] names = {"Robert", "John", "Julie", "Lora"};

  public Iterator getIterator() { return new NameIterator(); }

  private class NameIterator implements Iterator {
    int index; // the iterator holds the position, NOT the collection

    public boolean hasNext() { return index < names.length; }

    public Object next() {
      if (this.hasNext()) return names[index++];
      return null;
    }
  }
}

// Client — knows Container and Iterator, knows nothing about the array
NameRepository namesRepository = new NameRepository();
for (Iterator iter = namesRepository.getIterator(); iter.hasNext(); ) {
  String name = (String) iter.next();
  System.out.println("Name : " + name);
}
// → Name : Robert / Name : John / Name : Julie / Name : Lora
THE KEY DETAIL — index lives on the iterator, not on the collection. That is why you can hand out two iterators over the same NameRepository and have them at different positions at the same time.

Step-by-step: how to design one

Mnemonic — I.C.C.A.Iterator interface → Concrete iterators → Collection interface → Aggregate implementations.
  1. Identify and design the Iterator interface.
  2. For each class representing a collection data structure, design a concrete Iterator and associate it with that collection. Implement the concrete iterator's methods in terms of that data structure.
  3. Create the Collection interface, which includes the interface method that creates Iterators.
  4. For each collection class, implement the Aggregate interface to instantiate and return a concrete Iterator.

Benefits

  • A consistent way for clients to iterate over the objects in a collection.
  • Abstracts the internals — if the collection changes, clients do not have to change.
  • Extensible — many iterators can support different traversals over the same or different collection structures.

In-class exercise — The Company Directory

Build a corporate directory that stores employee records in two different internal structures (say an array and a list) but lets a client traverse both uniformly — and offer two traversal methods: standard and reverse. Then name the pattern, draw the class diagram, and generate the code.

The answer: Iterator. One EmployeeIterator interface; ArrayDirectory and ListDirectory both implement the Aggregate interface; and because traversal logic lives in the iterator, "reverse" is simply a second concrete iterator (ReverseIterator) over the same collection — no change to the collection classes at all. That last point is exactly benefit #3.

02

The Observer Pattern

Object Behavioral Pattern

Standardizes the operations between objects that interoperate using a one-to-many relationship. Intent: define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

Mnemonic — a newsletter subscription: you subscribe once, the publisher keeps a list, and every new issue reaches everyone on it automatically. The publisher never learns who you are beyond "someone with an inbox" — it just calls update().

The three words that matter

  • One-to-many — one Subject, many Observers.
  • Dependency — observers care about the subject's state; the subject does not care what they do with it.
  • Automatically — nobody polls. The subject pushes the notification the moment state changes.

Structure

Subject
−observers
+attach(o)
+detach(o)
+notifyAll()
holds 0..* Observers ♦
calls update() on each
Observer
+update()
ConcreteObserverA
+update()
ConcreteObserverB
+update()
READING THE DIAGRAM — the Subject holds a list of Observers and knows only the abstract Observer type. On a state change it walks its list and calls update() on each one. Adding a new kind of observer requires no change to the Subject — that is where the pattern's flexibility comes from.

Worked Example — one integer, three displays

Setup: a Subject holds a single state integer. Three observers — BinaryObserver, OctalObserver and HexaObserver — each render that same number in their own base. Set the state once, and all three print.

// The Subject — keeps the list, owns the notification
public class Subject {
  private List<Observer> observers = new ArrayList<>();
  private int state;

  public int getState() { return state; }

  public void setState(int state) {
    this.state = state;
    notifyAllObservers(); // state changed → tell everyone, automatically
  }

  public void attach(Observer observer) { observers.add(observer); }

  public void notifyAllObservers() {
    for (Observer observer : observers) observer.update(); // iterate + update
  }
}

// The Observer abstraction — one abstract update() method
public abstract class Observer {
  protected Subject subject;
  public abstract void update();
}

// A concrete observer — registers itself with the subject in its constructor
public class BinaryObserver extends Observer {
  public BinaryObserver(Subject subject) {
    this.subject = subject;
    this.subject.attach(this); // attach at construction time
  }
  public void update() {
    System.out.println("Binary String: " + Integer.toBinaryString(subject.getState()));
  }
}
// OctalObserver and HexaObserver are identical apart from the conversion call.

// Client — attach once, then just change the state
Subject subject = new Subject();
new HexaObserver(subject);
new OctalObserver(subject);
new BinaryObserver(subject);

subject.setState(15); // → Hex: F · Octal: 17 · Binary: 1111
subject.setState(10); // → Hex: A · Octal: 12 · Binary: 1010
THE KEY DETAIL — the client never calls update() and never touches the observers after construction. It changes one value — setState() — and the fan-out happens inside the Subject. Delete an observer or add a fourth one, and not a single line of Subject changes.

Step-by-step: how to apply it

Mnemonic — S.I.O.I.R.Subject interface → Inherit from it → Observer interface → Implement update() → Register at run time.
  1. Design the Subject interface and implement the code for attaching, detaching and notifying observer objects. Keeping track of observers can be done with an existing linked-list data structure.
  2. For classes that manage information of interest to observers, inherit from the Subject class created in step 1.
  3. Design the Observer interface, which includes the abstract update() interface method.
  4. For all observers in the system, implement the Observer interface — which requires implementing update().
  5. At run time, create each observer and attach it to the subject. When changes occur, the subject iterates through its list of registered objects and calls their update() method.

Benefits

  • Flexibility for adding new services to the system.
  • Because specific services are compartmentalized, maintaining and modifying existing system services becomes easier.

In-class activity — Social Media Followers

A simplified Instagram or X. The Subject is an Influencer who posts a status update and does not want to message every follower by hand. The Observers are followers who subscribed and want to be notified the moment a post goes live.

The task: give Influencer a follower list plus follow(), unfollow() and notifyFollowers(); define a Follower interface with update(String post); then implement MobileAppUser and WebBrowserUser, each printing something like "Mobile User received notification: [Post Content]".

Note the mapping: follow/unfollow are attach/detach, and notifyFollowers() is notifyAllObservers(). Same pattern, renamed for the domain.

03

Both, Side by Side

Same exam trick as last lecture: ask "what is this pattern actually solving?"

Pattern Solves Relationship Cue phrase
Iterator A client needs to walk a collection without knowing how it stores its elements One client ↔ one traversal at a time "access the elements sequentially without exposing the representation"
Observer Many objects need to react the moment one object's state changes One Subject → many Observers "one-to-many dependency, notified automatically"

Where they meet

Look again at notifyAllObservers(): the Subject iterates over its observer list to notify it. Observer uses iteration internally — which is why these two land in the same lecture and why an exam question can legitimately involve both at once.

Distinguishing them from the structural three

Category Concerned with This course's patterns
Creational How objects get created Abstract Factory, Factory Method, Singleton, Builder, Prototype
Structural How classes and objects are composed into larger structures Adapter, Composite, Facade
Behavioral How behavior and communication are distributed among objects Iterator, Observer
04

Quick-Fire Flashcards

Tap a card to flip it.

05

Practice Questions

Written from this lecture's content — self-check practice, not an official past paper.

06

One-Screen Recap