Low-level design2 min
Observer Pattern
Type: Behavioral Pattern
Relevance: Extremely High. This is the foundation of Event Listeners, Pub-Sub systems, and Reactive Programming (RxJS).
The Observer pattern lets you define a subscription mechanism to notify multiple objects about any events that happen to the object they're observing.
Real-Life Analogy
Think of a YouTube Channel. If you want to know when a channel uploads a new video, you don't go to the channel page every 5 minutes and check (Polling). That wastes your time and YouTube's servers. Instead, you click the Subscribe button (You become an Observer). When the channel (The Publisher) uploads a video, YouTube automatically sends a notification to you and every other subscriber.
The Problem
If you have a WeatherStation that reads temperature, and a PhoneApp and DesktopApp that need to display it. If the apps constantly poll the station, it wastes resources. If the station has hardcoded references to phoneApp.update() and desktopApp.update(), the station becomes tightly coupled to the UI.
How to Implement Observer
The Publisher (Subject) maintains a list of Observers (Subscribers). It provides methods to attach() or detach() observers. When its state changes, it loops through the list and calls observer.update().
Example in Code
// 1. The Observer Interface
interface Observer {
update(temperature: number): void;
}
// 2. The Subject Interface
interface Subject {
attach(observer: Observer): void;
detach(observer: Observer): void;
notify(): void;
}
// 3. The Concrete Subject (The Weather Station)
class WeatherStation implements Subject {
private temperature: number = 0;
private observers: Observer[] = [];
public attach(observer: Observer): void {
this.observers.push(observer);
}
public detach(observer: Observer): void {
this.observers = this.observers.filter(obs => obs !== observer);
}
public notify(): void {
for (const observer of this.observers) {
observer.update(this.temperature);
}
}
// State changes here!
public setTemperature(temp: number): void {
console.log(`\nWeatherStation: Temperature changed to ${temp}°C`);
this.temperature = temp;
this.notify(); // Automatically notify everyone!
}
}
// 4. Concrete Observers
class PhoneDisplay implements Observer {
public update(temperature: number): void {
console.log(`📱 Phone: The temp is now ${temperature}°C`);
}
}
class DesktopDisplay implements Observer {
public update(temperature: number): void {
console.log(`💻 Desktop: The temp is now ${temperature}°C`);
}
}
// Client Code
const station = new WeatherStation();
const phone = new PhoneDisplay();
const desktop = new DesktopDisplay();
// Subscribe to the station
station.attach(phone);
station.attach(desktop);
// The station changes state. Both displays update automatically!
station.setTemperature(25);
station.setTemperature(30);
// Unsubscribe
station.detach(phone);
station.setTemperature(20); // Only Desktop will update now.
Class Diagram
Why is it Important?
- Open/Closed Principle: You can introduce new subscriber classes without having to change the publisher's code.
- Loose Coupling: The publisher only knows that the subscriber implements a specific interface (
update). It doesn't know anything else about the concrete classes.