Definition

“Observer pattern defines a one-to-many dependency between objects so that when one object changes state, all of its dependents are notified and updated automatically”

Explanation

One object (Subject) maintains a list of observers and sends updates to them when its state changes. Observers implement a common interface and register with the subject. When the subject’s state updates (e.g. data model change), it calls notifyObservers(), causing each observer’s update method to run. This decouples subjects and observers.

Code

In this Python example, ConcreteSubject maintains a list of observers. Whenever its internal state changes (some_business_logic), it calls notify(), which in turn calls update() on each attached observer (ConcreteObserverA, ConcreteObserverB).
from abc import ABC, abstractmethod
from random import randrange
from typing import List

class Subject(ABC):
    @abstractmethod
    def attach(self, observer: "Observer") -> None:
        pass
    @abstractmethod
    def detach(self, observer: "Observer") -> None:
        pass
    @abstractmethod
    def notify(self) -> None:
        pass

class ConcreteSubject(Subject):
    _state: int = None
    _observers: List["Observer"] = []

    def attach(self, observer: "Observer") -> None:
        print("Subject: Attached an observer.")
        self._observers.append(observer)
    def detach(self, observer: "Observer") -> None:
        self._observers.remove(observer)
    def notify(self) -> None:
        print("Subject: Notifying observers...")
        for observer in self._observers:
            observer.update(self)
    def some_business_logic(self) -> None:
        print("\nSubject: I'm doing something important.")
        self._state = randrange(0, 10)
        print(f"Subject: My state has just changed to: {self._state}")
        self.notify()

class Observer(ABC):
    @abstractmethod
    def update(self, subject: Subject) -> None:
        pass

class ConcreteObserverA(Observer):
    def update(self, subject: Subject) -> None:
        if subject._state < 3:
            print("ConcreteObserverA: Reacted to the event")

class ConcreteObserverB(Observer):
    def update(self, subject: Subject) -> None:
        if subject._state == 0 or subject._state >= 2:
            print("ConcreteObserverB: Reacted to the event")

def main():
    subject = ConcreteSubject()

    observer_a = ConcreteObserverA()
    subject.attach(observer_a)
    observer_b = ConcreteObserverB()
    subject.attach(observer_b)

    subject.some_business_logic()
    subject.some_business_logic()

    subject.detach(observer_a)
    subject.some_business_logic()

if __name__ == "__main__":
    main()

Analogy

A news agency and subscribers. When breaking news happens (subject state change), the agency automatically pushes the news to all registered subscriber clients. Or a magazine subscription: one paper, many readers who get the new issue when released.

Interview Insights

Use Case: Event-driven systems (UI event listeners), data binding (e.g. MVC frameworks), messaging systems (pub/sub ), caching (invalidate caches when data changes). Useful when changes must propagate to many objects (e.g. model–view synchronization).
Advantages: Promotes loose coupling: subjects don’t need to know concrete observer classes. Makes it easy to add new observers at runtime. Simplifies broadcast of events
Disadvantages: Can lead to memory leaks if observers aren’t unsubscribed. Notification order is undefined. If many observers, notifications can slow down updates. Notification errors in one observer could affect others.
Common questions: “How do you prevent or remove observers (memory leak)?”, “How is this pattern implemented in your language (e.g. event listeners in Java/.NET)?”, “Difference between Observer and Mediator?”, “How to handle threading (e.g. asynchronous events)?”.\