Skip to main content
Design Patterns – Complete Beginner to Advanced Guide
CHAPTER 13 Intermediate

Observer Pattern

Updated: May 16, 2026
30 min read

# CHAPTER 13

Observer Pattern

1. Introduction

We have now entered the realm of Behavioral Patterns, which govern how objects communicate and assign responsibilities. Imagine an e-commerce app where a user clicks "Buy." When that happens, the OrderService must tell the EmailService to send a receipt, the InventoryService to deduct stock, and the AnalyticsService to log the sale. If the OrderService manually calls all three of those classes, it becomes tightly coupled to them. If you add a ShippingService tomorrow, you have to rewrite the OrderService. This is an architectural failure. The Observer Pattern is the legendary solution that enables modern, event-driven programming. In this chapter, we will master the Observer, exploring the Publish/Subscribe model, decoupled event streams, and real-time reactivity.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Define the intent and mechanics of the Observer (Pub/Sub) pattern.
  • Identify the tight-coupling dangers of synchronous method calls.
  • Understand the roles of the "Subject" (Publisher) and the "Observer" (Subscriber).
  • Architect a decoupled Event-Driven system.
  • Implement real-time notification arrays.

3. The Core Concept (Pub / Sub)

The Observer pattern defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
  • The Subject (The Publisher): The object that holds the interesting state. It maintains a list of "Subscribers" who want to know when it changes.
  • The Observers (The Subscribers): The objects that want to be notified. They all implement a standard interface (e.g., update()).
  • The Decoupling: The Publisher does not know *who* the Subscribers are. It just loops through its list and calls update() on everyone. If the InventoryService wants to know about orders, it subscribes. The OrderService never has to hardcode the word "Inventory."

4. Real-World Analogy

Think of a magazine subscription. The Publisher (The Magazine Company) does not care who you are. If you want the magazine, you add your address to their list (Subscribe). When a new edition is published, they loop through the list and send it to everyone. If you stop wanting it, you remove your address (Unsubscribe).

5. Why is this critical?

The Observer pattern is the absolute foundation of:
  • Event Listeners in UI: (e.g., button.addEventListener('click', function)). The button is the Publisher; the function is the Subscriber.
  • MVC Architecture: The Model (Subject) notifies the View (Observer) when the database changes.
  • Distributed Event Buses: (e.g., Kafka, RabbitMQ). These are just massive, cloud-scale implementations of the Observer pattern.

6. UML Diagram

*Observer Structure*
text
123456789
[ Subject (Publisher) ] ------------------> [ <<Interface>> Observer ]
- observers: List[]                               + update(data)
+ attach(Observer)                                      ^
+ detach(Observer)                                      | (Implements)
+ notify() { loop observers->update() }                 |
                                            -------------------------
                                            |                       |
                                    [ ConcreteObserverA ]   [ ConcreteObserverB ]
                                      + update(data)          + update(data)

7. Code Example (PHP)

Let's refactor the e-commerce Checkout process using the Observer pattern.
php
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
<?php
// --- 1. The Observer Interface (Subscriber) ---
interface OrderObserver {
    public function update($orderData);
}

// --- 2. The Subject Interface (Publisher) ---
interface Subject {
    public function attach(OrderObserver $observer);
    public function detach(OrderObserver $observer);
    public function notify();
}

// --- 3. The Concrete Subject (The Checkout Process) ---
class OrderService implements Subject {
    private $observers = [];
    public $latestOrder;

    // Add a subscriber to the list
    public function attach(OrderObserver $observer) {
        $this->observers[] = $observer;
    }

    public function detach(OrderObserver $observer) {
        // Find and remove observer (simplified for example)
        $key = array_search($observer, $this->observers);
        if ($key !== false) unset($this->observers[$key]);
    }

    // The Magic: Loop and notify everyone!
    public function notify() {
        foreach ($this->observers as $observer) {
            $observer->update($this->latestOrder);
        }
    }

    // Business Logic
    public function placeOrder($item) {
        echo "\nOrderService: Order placed for $item.\n";
        $this->latestOrder = $item;
        $this->notify(); // Trigger the event!
    }
}

// --- 4. Concrete Observers (The Decoupled Services) ---
class EmailService implements OrderObserver {
    public function update($orderData) {
        echo "EmailService: Sending receipt for $orderData to customer.\n";
    }
}

class InventoryService implements OrderObserver {
    public function update($orderData) {
        echo "InventoryService: Deducting stock for $orderData.\n";
    }
}

// --- 5. Client Code ---
$orderService = new OrderService();

// The services subscribe to the events
$orderService->attach(new EmailService());
$orderService->attach(new InventoryService());

// The OrderService executes, and the Observers react automatically!
$orderService->placeOrder("MacBook Pro");
// Output:
// OrderService: Order placed for MacBook Pro.
// EmailService: Sending receipt for MacBook Pro to customer.
// InventoryService: Deducting stock for MacBook Pro.
?>

8. Best Practices

  • Push vs. Pull:
  • *Push Model:* The Publisher sends the exact data payload to the update($data) method (as seen above). Best for simple data.
  • *Pull Model:* The Publisher just calls update(), and passes *itself* (update($this)). The Observer then uses getters to pull the specific data it wants. Best for complex state changes.

9. Common Mistakes

  • The Memory Leak (Lapsed Listener Problem): If an Observer subscribes to a Subject that lives forever (like a global UI manager), but the Observer object is destroyed, the Subject's internal array still holds a memory reference to the dead Observer. This prevents the Garbage Collector from cleaning it up, causing massive memory leaks. *The Fix:* Always explicitly detach() or use Weak References.

10. Mini Project: Build a Stock Ticker

  1. 1. Subject: Create a StockTicker class with an attach() method and a setPrice($price) method that triggers notify().
  1. 2. Observers: Create a MobileAppDashboard and a WallStreetTerminal that both implement StockObserver.
  1. 3. Action: Attach both observers. Call setPrice(150.00). Both dashboards should independently print out the new price simultaneously.

11. Practice Exercises

  1. 1. Define the relationship between the "Subject" and the "Observer." How does this pattern effectively decouple the core business logic from ancillary services?
  1. 2. Explain the "Lapsed Listener Problem." Why is it critical to explicitly unsubscribe (detach) observers in long-running applications?

12. MCQs with Answers

Question 1

In the Observer design pattern, what is the primary architectural responsibility of the "Subject" (Publisher) class?

Question 2

Modern Javascript UI frameworks (like React or Vue) and basic DOM manipulation rely heavily on addEventListener('click', myFunc). What classic design pattern is this an exact implementation of?

13. Interview Questions

  • Q: Compare and contrast the "Push" model versus the "Pull" model in the Observer pattern. What are the bandwidth and coupling trade-offs of each approach?
  • Q: A monolithic application is experiencing massive latency because the Checkout function makes 10 sequential, synchronous method calls to different services. Explain how implementing the Observer pattern (an Event Bus) resolves this tightly coupled bottleneck.
  • Q: Walk me through the exact structural mechanics of the Model-View-Controller (MVC) architecture. Which of the GoF design patterns is utilized to ensure the View updates automatically when the Model's data changes?

14. FAQs

Q: Is the Observer pattern synchronous or asynchronous? A: In standard OOP (like the PHP example above), the notify() loop is entirely synchronous. It blocks the main thread until all observers finish. For true asynchronous behavior (non-blocking), you must combine the Observer pattern with a Message Queue or asynchronous worker threads.

15. Summary

In Chapter 13, we revolutionized how our objects communicate. We abandoned the rigid, tightly coupled nightmare of synchronous method calls, embracing the dynamic reactivity of Event-Driven programming. We established the Publisher/Subscriber relationship of the Observer pattern, allowing core systems to broadcast their state changes blindly, while dozens of independent modules react simultaneously. We recognized this pattern as the foundational bedrock of modern UI frameworks, MVC architecture, and distributed event systems.

16. Next Chapter Recommendation

Our objects can communicate reactively. But what if an object needs to completely change its core algorithm dynamically at runtime? Proceed to Chapter 14: Strategy Pattern.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·