Card 03/ 09
Observer Template in Python, Java and TypeScript
Attach two observers, change the subject, and watch both react.
The Observer template is the pattern in its pure form, with the participant names from the class diagram. Two observers attach to a subject, the state changes and both react, then one detaches and only the other hears the second change. The steps add each participant in Python, and the tabs give the complete program in three languages, each with its output.
Building the template one participant at a time
Observer
Declares the one method the subject calls.
class Observer(ABC):
@abstractmethod
def update(self, subject: "Subject") -> None: ...Subject
Keeps a list, and calls every observer on it.
def attach(self, observer: Observer) -> None:
self._observers.append(observer)
def notify(self) -> None:
for observer in self._observers:
observer.update(self)The change
Setting the state is what triggers the notification.
@state.setter
def state(self, value: int) -> None:
self._state = value
self.notify()Concrete observers
React by reading the state they were told about.
class ConcreteObserverA(Observer):
def update(self, subject: Subject) -> None:
print(f"A saw {subject.state}")The complete template
from abc import ABC, abstractmethod
class Observer(ABC):
@abstractmethod
def update(self, subject: "Subject") -> None: ...
class Subject:
def __init__(self) -> None:
self._observers: list[Observer] = []
self._state = 0
def attach(self, observer: Observer) -> None:
self._observers.append(observer)
def detach(self, observer: Observer) -> None:
self._observers.remove(observer)
def notify(self) -> None:
for observer in self._observers:
observer.update(self)
@property
def state(self) -> int:
return self._state
@state.setter
def state(self, value: int) -> None:
self._state = value
self.notify()
class ConcreteObserverA(Observer):
def update(self, subject: Subject) -> None:
print(f"A saw {subject.state}")
class ConcreteObserverB(Observer):
def update(self, subject: Subject) -> None:
print(f"B saw {subject.state}")
subject = Subject()
a, b = ConcreteObserverA(), ConcreteObserverB()
subject.attach(a)
subject.attach(b)
subject.state = 1
subject.detach(b)
subject.state = 2A saw 1
B saw 1
A saw 2import java.util.ArrayList;
import java.util.List;
public class ObTemplate {
public static void main(String[] args) {
Subject subject = new Subject();
Observer a = new ConcreteObserverA();
Observer b = new ConcreteObserverB();
subject.attach(a);
subject.attach(b);
subject.setState(1);
subject.detach(b);
subject.setState(2);
}
}
interface Observer {
void update(Subject subject);
}
class Subject {
private final List<Observer> observers = new ArrayList<>();
private int state;
void attach(Observer observer) {
observers.add(observer);
}
void detach(Observer observer) {
observers.remove(observer);
}
void notifyObservers() {
for (Observer observer : observers) {
observer.update(this);
}
}
int getState() {
return state;
}
void setState(int state) {
this.state = state;
notifyObservers();
}
}
class ConcreteObserverA implements Observer {
public void update(Subject subject) {
System.out.println("A saw " + subject.getState());
}
}
class ConcreteObserverB implements Observer {
public void update(Subject subject) {
System.out.println("B saw " + subject.getState());
}
}A saw 1
B saw 1
A saw 2interface Observer {
update(subject: Subject): void;
}
class Subject {
private readonly observers: Observer[] = [];
private value = 0;
attach(observer: Observer): void {
this.observers.push(observer);
}
detach(observer: Observer): void {
this.observers.splice(this.observers.indexOf(observer), 1);
}
notify(): void {
for (const observer of this.observers) {
observer.update(this);
}
}
get state(): number {
return this.value;
}
set state(value: number) {
this.value = value;
this.notify();
}
}
class ConcreteObserverA implements Observer {
update(subject: Subject): void {
console.log(`A saw ${subject.state}`);
}
}
class ConcreteObserverB implements Observer {
update(subject: Subject): void {
console.log(`B saw ${subject.state}`);
}
}
const subject = new Subject();
const a = new ConcreteObserverA();
const b = new ConcreteObserverB();
subject.attach(a);
subject.attach(b);
subject.state = 1;
subject.detach(b);
subject.state = 2;A saw 1
B saw 1
A saw 2The first change reaches both observers, in the order they attached. After detach(b) the second change reaches only A, so B saw 2 never appears. The subject's code names neither observer class: it holds a list typed by the interface, and a third observer would attach without any edit to Subject.
← → move through sections and cards