Observer
Intent
Section titled “Intent”Observer defines a one-to-many dependency between objects so that when one object — the subject — changes state, all of its dependents are notified and updated automatically. The subject keeps a list of observers and broadcasts to them without knowing their concrete types.
Problem
Section titled “Problem”Suppose a newsletter publishes new articles, and several things must react: an email sender, a log, a live feed on the homepage. The crude approach is to have the publisher call each of them directly. But then the publisher must import and know every consumer, and every time you add or remove a consumer you edit the publisher. The publisher becomes a hub coupled to everything downstream.
The variation here is who is listening, and that set changes over time. Observer inverts the relationship: consumers register themselves with the subject, and the subject broadcasts to whatever list it currently holds. The publisher knows only the observer interface, so subscribers can come and go at runtime and the publisher never changes.
Structure
Section titled “Structure”classDiagram
class Subject {
-observers: List~Observer~
+subscribe(o)
+unsubscribe(o)
+notify(news)
}
class Observer {
<<interface>>
+update(news)
}
class EmailSubscriber {
+update(news)
}
class FeedSubscriber {
+update(news)
}
Subject o--> Observer
Observer <|.. EmailSubscriber
Observer <|.. FeedSubscriber - Subject — holds the list of observers and exposes subscribe, unsubscribe, and a way to notify. When its state changes it broadcasts to all observers.
- Observer — the interface every dependent implements, typically a single update method that receives the change.
- Concrete Observer — one dependent; it reacts to the update however it likes.
- Client — registers and removes observers and triggers state changes on the subject.
Example
Section titled “Example”A newsletter subject that broadcasts each new headline to whatever subscribers are currently registered. Each language uses its idiomatic callback or interface mechanism.
interface Observer { update(headline: string): void;}
class Newsletter { private observers = new Set<Observer>();
subscribe(o: Observer): void { this.observers.add(o); } unsubscribe(o: Observer): void { this.observers.delete(o); } publish(headline: string): void { for (const o of this.observers) { o.update(headline); } }}
class EmailSubscriber implements Observer { constructor(private readonly who: string) {} update(headline: string): void { console.log(`Email to ${this.who}: ${headline}`); }}
const news = new Newsletter();const alice = new EmailSubscriber('alice');news.subscribe(alice);news.subscribe(new EmailSubscriber('bob'));news.publish('Observer explained'); // both notifiednews.unsubscribe(alice);news.publish('Now without alice'); // only bobfrom typing import Protocol
class Observer(Protocol): def update(self, headline: str) -> None: ...
class Newsletter: def __init__(self) -> None: self._observers: list[Observer] = []
def subscribe(self, o: Observer) -> None: self._observers.append(o)
def unsubscribe(self, o: Observer) -> None: self._observers.remove(o)
def publish(self, headline: str) -> None: for o in list(self._observers): o.update(headline)
class EmailSubscriber: def __init__(self, who: str) -> None: self.who = who
def update(self, headline: str) -> None: print(f"Email to {self.who}: {headline}")
news = Newsletter()alice = EmailSubscriber("alice")news.subscribe(alice)news.subscribe(EmailSubscriber("bob"))news.publish("Observer explained") # both notifiednews.unsubscribe(alice)news.publish("Now without alice") # only bobpackage main
import "fmt"
// Observer is implemented by anything that wants to be notified.type Observer interface { Update(headline string)}
type Newsletter struct { observers []Observer}
func (n *Newsletter) Subscribe(o Observer) { n.observers = append(n.observers, o)}
func (n *Newsletter) Publish(headline string) { for _, o := range n.observers { o.Update(headline) }}
type EmailSubscriber struct{ Who string }
func (e EmailSubscriber) Update(headline string) { fmt.Printf("Email to %s: %s\n", e.Who, headline)}
func main() { news := &Newsletter{} news.Subscribe(EmailSubscriber{Who: "alice"}) news.Subscribe(EmailSubscriber{Who: "bob"}) news.Publish("Observer explained") // both notified}trait Observer { fn update(&self, headline: &str);}
struct Newsletter { observers: Vec<Box<dyn Observer>>,}impl Newsletter { fn new() -> Self { Newsletter { observers: Vec::new() } } fn subscribe(&mut self, o: Box<dyn Observer>) { self.observers.push(o); } fn publish(&self, headline: &str) { for o in &self.observers { o.update(headline); } }}
struct EmailSubscriber { who: String,}impl Observer for EmailSubscriber { fn update(&self, headline: &str) { println!("Email to {}: {}", self.who, headline); }}
fn main() { let mut news = Newsletter::new(); news.subscribe(Box::new(EmailSubscriber { who: "alice".into() })); news.subscribe(Box::new(EmailSubscriber { who: "bob".into() })); news.publish("Observer explained"); // both notified}When to use / trade-offs
Section titled “When to use / trade-offs”- Pro: the subject and its observers are loosely coupled — the subject knows only the observer interface.
- Pro: observers can be added or removed at runtime, including dynamically as the program runs.
- Pro: it is the backbone of event systems, UI bindings, and reactive data flows.
- Con: notification order is usually unspecified, so observers must not depend on running in a fixed sequence.
- Con: unremoved observers cause memory leaks, and a long notification chain can be hard to trace or debug.
Related patterns
Section titled “Related patterns”- Mediator also decouples senders from receivers but routes through a central hub rather than direct broadcast.
- Command is often the payload an observer receives, turning a notification into an executable action.