Instructor: Stefan Mitsch
// a button notifies subscribers when it is pressed JButton b = new JButton("Hello, world!"); // a subscriber, implemented with an anonymous class b.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { System.out.println("Hello, anonymous world!"); } }); // a subscriber, implemented with a lambda expression b.addActionListener(e -> System.out.println("Hello, lambda world!"));
Problem
Avoid frequent status requests or unsolicitated calls
Intent
Structure
The Publisher issues events to other objects when the state of the publisher changes or certain behavior executes. Publishers provide a subscription infrastructure for new subscribers to join and current subscribers to leave.
Publisher
When a new event happens, the publisher notifies all subscribed subscribers.
The Subscriber interface declares methods how subscribers can be notified (often a single update method); the method may have parameters for event details.
Subscriber
update
ConcreteSubscriber classes perform some action in response to being notified.
ConcreteSubscriber
Often, subscribers need context information to handle the notification correctly. The publisher can either pass context data as argument to the update method, or pass itself so that subscribers can fetch the necessary context.
The Client creates publishers and subscribers separately and registers subscribers for events.
Client
Code Smell: Subclasses are hard-coded to notify a specific object
Before: A subclass per object to notify, tightly coupled publisher and subscriber
subscribe
unsubscribe
List<Integer> values = List.of(1, 2, 3, 4, 5); // iterate with explicit iterator Iterator<Integer> it = values.iterator(); while (it.hasNext()) { System.out.println(it.next()); } // iterate with for-expression for (int v : values) System.out.println(v);
Traverse elements of an aggregate object sequentially without exposing its underlying representation
The Iterator interface declares the traversal operations.
Iterator
ConcreteIterator objects implement specific traversal operations (e.g., depth first); they track the iteration state. Multiple iterators can traverse the same aggregate object.
ConcreteIterator
The Aggregate interface declares methods for getting iterators.
Aggregate
ConcreteAggregate objects return new concrete iterators each time a client requests one.
ConcreteAggregate
The Client works with both collections and iterators through their interfaces, so that it avoids being coupled to concrete implementations. A client does not create an iterator itself, it gets it from a collection.
// client creates a builder StringBuilder s = new StringBuilder(); // director knows the steps of building the object s.append("Hello"); s.append(","); s.append(" "); s.append("world"); // client fetches the final product String str = s.toString();
// client creates a builder Stream.Builder<Int> builder = Stream.builder(); // director knows the steps of building the object builder. add(1). add(2). add(3); // client fetches the final product Stream<Int> values = builder.build();
Avoid tight coupling between object creation and object representation
The Builder interface declares construction steps.
Builder
ConcreteBuilder objects provide different implementations of construction steps and create concrete Product objects.
ConcreteBuilder
Product
Product objects are the results of production. Different builders can create different concrete products.
The Director class decides which construction steps to use in which order.
Director
The Client associates a builder object with a director; the director uses this builder for all construction steps.