Skip to content

Observer Pattern: Event-Driven Communication

Tell many objects when something changed

Builds on Interfaces and Coupling and Cohesion

Stage 3core2 min read

The Observer Pattern Solution

The refactored code implements the Observer Pattern, a behavioral design pattern. It gives an object a subscription list, so many other objects can be told whenever something happens to it.


1. How the Observer Pattern Solves the Problem

By introducing a formal subscription mechanism, the Subject (the YouTube Channel) no longer depends on the Observers (Subscribers). Each side has its own job. The channel only deals with its own changes. The subscribers decide how to notify.

Problem in Old ApproachHow Observer Pattern Solves It
Channel is tightly coupled with notification logicEach subscriber handles its own notification via the update() method.
Not extensible for new notification typesYou can add new subscriber classes (e.g., SMS, Slack) without modifying existing code.
No reusability of logicNotification logic sits inside reusable subscriber classes, not inside the channel.
SRP Violation (Upload + Notify in one class)Upload logic stays in YouTubeChannel; notification logic is external.
Difficult to manage large numbers of subscribersThe subscribe() and unsubscribe() methods add and remove subscribers at runtime.

2. Key Improvements

  • Dynamic Subscriptions: Subscribers can be added (subscribe) or removed (unsubscribe) at runtime.
  • Encapsulation: The YouTubeChannel no longer knows email addresses or app usernames. It only holds a list of Subscriber objects and calls update on each.
  • Adherence to SOLID:
    • Open/Closed Principle: You can add new subscriber types, and the YouTubeChannel class stays unchanged.
    • Single Responsibility Principle: Managing videos and managing notifications are now separate responsibilities.

3. Structural Breakdown

ComponentResponsibility
Subject Interface (Channel)Lists the methods for adding, removing and notifying observers.
Concrete Subject (YouTubeChannel)Holds the data observers care about. When it changes, it notifies its observers.
Observer Interface (Subscriber)Declares the update method that every object to be notified must have.
Concrete Observers (EmailSubscriber, etc.)Implements update to react when the subject changes.

4. Summary of the "Subscription Model"

The Observer Pattern works like a Newspaper Subscription. The publisher (Subject) does not know who reads the paper. It only has a list of addresses. When a new edition is ready, it sends a copy to everyone on the list. To stop receiving it, you remove yourself from the list. The publisher's main job, printing news, stays the same.

In this topic

After code1 file

After/Main.java124 linesGitHub
package DesignPatterns.BehaviouralPatterns.ObserverPattern.After;import java.util.*;/** * 1. THE OBSERVER INTERFACE * This defines the 'Contract'. Any object that wants to listen to * the channel must implement this interface. */interface Subscriber {    // The subject calls this method to push updates to the observer    void update(String videoTitle);}/** * 2. CONCRETE OBSERVERS * These classes implement the actual notification logic (Email, SMS, App). * The Subject doesn't need to know these classes exist. */class EmailSubscriber implements Subscriber {    private String email;    public EmailSubscriber(String email) {        this.email = email;    }    @Override    public void update(String videoTitle) {        System.out.println(            "Email sent to " + email + ": New video uploaded - " + videoTitle        );    }}class MobileAppSubscriber implements Subscriber {    private String username;    public MobileAppSubscriber(String username) {        this.username = username;    }    @Override    public void update(String videoTitle) {        System.out.println(            "In-app notification for " +                username +                ": New video - " +                videoTitle        );    }}/** * 3. THE SUBJECT INTERFACE * Defines how to manage the lifecycle of a subscription. */interface Channel {    void subscribe(Subscriber subscriber);    void unsubscribe(Subscriber subscriber);    void notifySubscribers(String videoTitle);}/** * 4. CONCRETE SUBJECT * Maintains a dynamic list of observers. It is now completely agnostic * to how notifications are sent. */class YouTubeChannel implements Channel {    // Dynamic list allows adding/removing subscribers at runtime    private List<Subscriber> subscribers = new ArrayList<>();    private String channelName;    public YouTubeChannel(String channelName) {        this.channelName = channelName;    }    @Override    public void subscribe(Subscriber subscriber) {        subscribers.add(subscriber);    }    @Override    public void unsubscribe(Subscriber subscriber) {        subscribers.remove(subscriber);    }    @Override    public void notifySubscribers(String videoTitle) {        // Broadcasters the update to everyone on the list        for (Subscriber subscriber : subscribers) {            subscriber.update(videoTitle);        }    }    /**     * BUSINESS LOGIC     * When the state changes (upload), we simply trigger the notification loop.     */    public void uploadVideo(String videoTitle) {        System.out.println(channelName + " uploaded: " + videoTitle + "\n");        notifySubscribers(videoTitle);    }}class Main {    public static void main(String[] args) {        YouTubeChannel tuf = new YouTubeChannel("takeUforward");        // RUNTIME FLEXIBILITY:        // We can create and add any number of different observers        // without ever touching the YouTubeChannel class again.        tuf.subscribe(new MobileAppSubscriber("raj"));        tuf.subscribe(new EmailSubscriber("rahul@example.com"));        tuf.uploadVideo("Observer-Pattern-Tutorial");        // Dynamic Unsubscribe        // tuf.unsubscribe(someSubscriber);    }}

Done with this topic?

How well do you know it?