- Published on
Design Patterns: Observer
- Authors
- Name
- Loi Tran
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.
from abc import ABC, abstractmethod
# Subject
class Subject:
def __init__(self):
self._observers = []
def attach(self, observer):
self._observers.append(observer)
def detach(self, observer):
self._observers.remove(observer)
def notify(self):
for observer in self._observers:
observer.update()
# Observer
class Observer(ABC):
@abstractmethod
def update(self):
pass
# Concrete Observers
class ConcreteObserverA(Observer):
def update(self):
print("Observer A: Reacted to the event.")
class ConcreteObserverB(Observer):
def update(self):
print("Observer B: Reacted to the event.")
# Usage
subject = Subject()
observer_a = ConcreteObserverA()
observer_b = ConcreteObserverB()
subject.attach(observer_a)
subject.attach(observer_b)
subject.notify() # Both observers react
subject.detach(observer_a)
subject.notify() # Only observer B reacts
// Subject
class Subject {
constructor() {
this.observers = [];
}
attach(observer) {
this.observers.push(observer);
}
detach(observer) {
this.observers = this.observers.filter(obs => obs !== observer);
}
notify() {
this.observers.forEach(observer => observer.update());
}
}
// Observer
class Observer {
update() {
throw new Error("Method 'update()' must be implemented.");
}
}
// Concrete Observers
class ConcreteObserverA extends Observer {
update() {
console.log("Observer A: Reacted to the event.");
}
}
class ConcreteObserverB extends Observer {
update() {
console.log("Observer B: Reacted to the event.");
}
}
// Usage
const subject = new Subject();
const observerA = new ConcreteObserverA();
const observerB = new ConcreteObserverB();
subject.attach(observerA);
subject.attach(observerB);
subject.notify(); // Both observers react
subject.detach(observerA);
subject.notify(); // Only Observer B reacts
// Subject
class Subject {
final List<Observer> _observers = [];
void attach(Observer observer) {
_observers.add(observer);
}
void detach(Observer observer) {
_observers.remove(observer);
}
void notify() {
for (var observer in _observers) {
observer.update();
}
}
}
// Observer
abstract class Observer {
void update();
}
// Concrete Observers
class ConcreteObserverA implements Observer {
void update() {
print("Observer A: Reacted to the event.");
}
}
class ConcreteObserverB implements Observer {
void update() {
print("Observer B: Reacted to the event.");
}
}
// Usage
void main() {
final subject = Subject();
final observerA = ConcreteObserverA();
final observerB = ConcreteObserverB();
subject.attach(observerA);
subject.attach(observerB);
subject.notify(); // Both observers react
subject.detach(observerA);
subject.notify(); // Only Observer B reacts
}
# Subject
class Subject
def initialize
@observers = []
end
def attach(observer)
@observers << observer
end
def detach(observer)
@observers.delete(observer)
end
def notify
@observers.each(&:update)
end
end
# Observer
class Observer
def update
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
end
# Concrete Observers
class ConcreteObserverA < Observer
def update
puts "Observer A: Reacted to the event."
end
end
class ConcreteObserverB < Observer
def update
puts "Observer B: Reacted to the event."
end
end
# Usage
subject = Subject.new
observer_a = ConcreteObserverA.new
observer_b = ConcreteObserverB.new
subject.attach(observer_a)
subject.attach(observer_b)
subject.notify # Both observers react
subject.detach(observer_a)
subject.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.