← Back to Home

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.

What is it?

Observer Pattern **eliminates tight coupling** and **prevents missed notifications** by automatically distributing events to all subscribers.

Demo: The Notification Distribution Problem

You need to notify Email, SMS, Push, and Slack services when events happen. Watch what happens when you add services and publish events:

👀 Observer Pattern

Automatic notification to ALL subscribers

Add Notification Services:
Active Subscribers (0):
No subscribers yet...
Publish Events:
Event Log:
No activity yet...

📧 Manual Notification

Hardcoded logic - misses notifications!

Add Notification Services:
Active Services (0):
No services yet...
Publish Events:
Event Log:
No activity yet...

💡 The Key Difference:

  • • **Observer Pattern**: Automatically notifies ALL subscribers - never misses anyone
  • • **Manual Notification**: Hardcoded logic often forgets services (notice Slack gets missed!)
  • • Try adding Slack and publishing events - see how manual approach fails!

Code Example: The Real Problem

// Observer Pattern - Add once, notify everywhere automatically
class EventPublisher {
  private observers: NotificationObserver[] = [];

  subscribe(observer: NotificationObserver) {
    this.observers.push(observer);
  }

  publishEvent(event: string) {
    // AUTOMATIC: All observers get notified
    this.observers.forEach(observer => {
      observer.notify(event); // Works automatically
    });
  }
}

// Usage - New services automatically get ALL events:
publisher.subscribe(new EmailObserver());
publisher.subscribe(new SMSObserver());
publisher.subscribe(new SlackObserver());   // Add once
publisher.subscribe(new DiscordObserver()); // Add once

publisher.publishEvent("Order Placed"); // ALL 4 services notified automatically

// vs Manual Notification - Easy to miss services
class ManualNotificationSystem {
  publishEvent(event: string) {
    // MANUAL: Must remember every service type
    if (this.emailService) this.emailService.notify(event);
    if (this.smsService) this.smsService.notify(event);
    if (this.pushService) this.pushService.notify(event);
    // OOPS! Forgot Slack again!
    // OOPS! Forgot Discord again!
  }
}

// PROBLEM: Every event publisher must manually list all services
class OrderService {
  processOrder() {
    // Must manually notify each service - easy to miss one
    this.emailService.notify("Order Placed");
    this.smsService.notify("Order Placed");
    // Forgot push and slack notifications!
  }
}

class PaymentService {
  processPayment() {
    // Must manually notify each service - easy to miss one
    this.emailService.notify("Payment Processed");
    // Forgot SMS, push, and slack notifications!
  }
}

// RESULT:
// Observer: Subscribe once → get ALL events automatically
// Manual: Update every event publisher → high chance of missing notifications

Common Uses

  • Event handling systems (DOM events, custom application events)
  • Notification systems (email, SMS, push, Slack, Discord)
  • Model-View architectures (data changes automatically update UI)
  • Reactive programming (state changes trigger automatic updates)
  • Pub-sub message systems (microservices, event-driven architecture)

When to Use

  • When multiple objects need to stay in sync with one object
  • You want to avoid tight coupling between event publishers and subscribers
  • When you need to broadcast changes to an unknown number of objects
  • To prevent missed notifications in complex systems

Caution

  • Can cause memory leaks if subscribers aren't properly cleaned up
  • Too many observers can affect performance
  • Debugging can be harder due to indirect relationships
  • Unexpected update sequences can cause issues if not carefully designed