Design Patterns: Observer

Introduction

The Observer pattern is a behavioral design pattern that defines a one-to-many dependency between objects. When the state of one object (the subject) changes, all its dependent objects (observers) are automatically notified and updated. This pattern promotes loose coupling, allowing objects to interact without needing to know the details of each other’s implementation.

Purpose

One-to-many relationship where one change is observed and triggers updates to its dependents.

Use Cases

  • Event handling
  • Email notifications
  • Live updates.
1from abc import ABC, abstractmethod
2
3# Subject
4class Subject:
5 def __init__(self):
6 self._observers = []
7
8 def attach(self, observer):
9 self._observers.append(observer)
10
11 def detach(self, observer):
12 self._observers.remove(observer)
13
14 def notify(self):
15 for observer in self._observers:
16 observer.update()
17
18# Observer
19class Observer(ABC):
20 @abstractmethod
21 def update(self):
22 pass
23
24# Concrete Observers
25class ConcreteObserverA(Observer):
26 def update(self):
27 print("Observer A: Reacted to the event.")
28
29class ConcreteObserverB(Observer):
30 def update(self):
31 print("Observer B: Reacted to the event.")
32
33# Usage
34subject = Subject()
35observer_a = ConcreteObserverA()
36observer_b = ConcreteObserverB()
37
38subject.attach(observer_a)
39subject.attach(observer_b)
40
41subject.notify() # Both observers react
42subject.detach(observer_a)
43subject.notify() # Only observer B reacts
1// Subject
2class Subject {
3 constructor() {
4 this.observers = [];
5 }
6
7 attach(observer) {
8 this.observers.push(observer);
9 }
10
11 detach(observer) {
12 this.observers = this.observers.filter(obs => obs !== observer);
13 }
14
15 notify() {
16 this.observers.forEach(observer => observer.update());
17 }
18}
19
20// Observer
21class Observer {
22 update() {
23 throw new Error("Method 'update()' must be implemented.");
24 }
25}
26
27// Concrete Observers
28class ConcreteObserverA extends Observer {
29 update() {
30 console.log("Observer A: Reacted to the event.");
31 }
32}
33
34class ConcreteObserverB extends Observer {
35 update() {
36 console.log("Observer B: Reacted to the event.");
37 }
38}
39
40// Usage
41const subject = new Subject();
42const observerA = new ConcreteObserverA();
43const observerB = new ConcreteObserverB();
44
45subject.attach(observerA);
46subject.attach(observerB);
47
48subject.notify(); // Both observers react
49subject.detach(observerA);
50subject.notify(); // Only Observer B reacts
1// Subject
2class Subject {
3 final List<Observer> _observers = [];
4
5 void attach(Observer observer) {
6 _observers.add(observer);
7 }
8
9 void detach(Observer observer) {
10 _observers.remove(observer);
11 }
12
13 void notify() {
14 for (var observer in _observers) {
15 observer.update();
16 }
17 }
18}
19
20// Observer
21abstract class Observer {
22 void update();
23}
24
25// Concrete Observers
26class ConcreteObserverA implements Observer {
27
28 void update() {
29 print("Observer A: Reacted to the event.");
30 }
31}
32
33class ConcreteObserverB implements Observer {
34
35 void update() {
36 print("Observer B: Reacted to the event.");
37 }
38}
39
40// Usage
41void main() {
42 final subject = Subject();
43 final observerA = ConcreteObserverA();
44 final observerB = ConcreteObserverB();
45
46 subject.attach(observerA);
47 subject.attach(observerB);
48
49 subject.notify(); // Both observers react
50 subject.detach(observerA);
51 subject.notify(); // Only Observer B reacts
52}
1# Subject
2class Subject
3 def initialize
4 @observers = []
5 end
6
7 def attach(observer)
8 @observers << observer
9 end
10
11 def detach(observer)
12 @observers.delete(observer)
13 end
14
15 def notify
16 @observers.each(&:update)
17 end
18end
19
20# Observer
21class Observer
22 def update
23 raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
24 end
25end
26
27# Concrete Observers
28class ConcreteObserverA < Observer
29 def update
30 puts "Observer A: Reacted to the event."
31 end
32end
33
34class ConcreteObserverB < Observer
35 def update
36 puts "Observer B: Reacted to the event."
37 end
38end
39
40# Usage
41subject = Subject.new
42observer_a = ConcreteObserverA.new
43observer_b = ConcreteObserverB.new
44
45subject.attach(observer_a)
46subject.attach(observer_b)
47
48subject.notify # Both observers react
49subject.detach(observer_a)
50subject.notify # Only Observer B reacts

Conclusion

The Observer pattern is ideal for scenarios where multiple components need to stay synchronized with a single source of truth. It is commonly used in event handling systems, GUI frameworks, messaging systems, and any situation where changes in one object should automatically propagate to others, improving maintainability and scalability.