Observer Pattern

The Observer is a behavioral design pattern that defines a one-to-many dependency between objects: when one object (subject) changes state, all its dependents (observers) are notified and updated automatically. It promotes loose coupling by allowing observers to subscribe/unsubscribe dynamically, without the subject knowing their details.

Key Principles

  • Publish-Subscribe: Subject publishes changes; observers subscribe.
  • Loose Coupling: Subject unaware of observer implementations.
  • Dynamic: Observers can attach/detach at runtime.

Structure

  • Subject: Maintains observer list; methods: attach(), detach(), notify().
  • Concrete Subject: Tracks state; calls notify on change.
  • Observer: Interface with update() method.
  • Concrete Observer: Implements update; reacts to notifications.

Python Example: News Publisher (Subject-Observer)

from abc import ABC, abstractmethod
from typing import List

# Observer Interface
class Observer(ABC):
    @abstractmethod
    def update(self, message: str):
        pass

# Concrete Observers
class EmailObserver(Observer):
    def __init__(self, email: str):
        self.email = email

    def update(self, message: str):
        print(f"Sending email to {self.email}: {message}")

class SMSObserver(Observer):
    def __init__(self, phone: str):
        self.phone = phone

    def update(self, message: str):
        print(f"Sending SMS to {self.phone}: {message}")

# Subject
class NewsPublisher:
    def __init__(self):
        self._observers: List[Observer] = []
        self._message = None

    def attach(self, observer: Observer):
        self._observers.append(observer)

    def detach(self, observer: Observer):
        self._observers = [obs for obs in self._observers if obs != observer]

    def notify(self):
        for observer in self._observers:
            observer.update(self._message)

    def set_message(self, message: str):
        self._message = message
        self.notify()  # Auto-notify on change

# Usage (Client Code)
if __name__ == "__main__":
    publisher = NewsPublisher()

    email_obs = EmailObserver("alice@example.com")
    sms_obs = SMSObserver("+1234567890")

    publisher.attach(email_obs)
    publisher.attach(sms_obs)

    publisher.set_message("Breaking news: Python 3.13 released!")  # Triggers notifications

    publisher.detach(email_obs)
    publisher.set_message("Update: New features added!")  # Only SMS notified

Output:

Sending email to alice@example.com: Breaking news: Python 3.13 released!
Sending SMS to +1234567890: Breaking news: Python 3.13 released!
Sending SMS to +1234567890: Update: New features added!

When to Use Observer

  • Event-Driven Systems: UI updates, pub-sub (e.g., MVC models).
  • Dynamic Notifications: Subscribe/unsubscribe at runtime.
  • Avoid: If few observers or tight coupling needed.

Summary: Observer enables automatic notifications from subject to multiple observers on state changes, fostering decoupled, event-based designs.