LLD
Machine Coding
Interview Course
Java โ€ข Interview Prep
Lesson 5 of 6
Prerequisites

Observer Pattern


What is it? (The Analogy)

Think about a YouTube channel. When you subscribe to a channel, you automatically get notified whenever a new video is uploaded. You don't have to keep checking the channel page every 5 minutes -- YouTube pushes the notification to you. You subscribed once, and now you're automatically kept in the loop.

Here's the beauty: the YouTube creator doesn't need to know WHO is subscribed. They just upload a video, and YouTube notifies EVERYONE on the subscriber list. If 10 people subscribe, 10 get notified. If 10 million subscribe, 10 million get notified. The creator's job doesn't change.

And the best part? You can unsubscribe any time. You stop getting notifications, but the channel keeps working for everyone else.

That's the Observer Pattern: one object (the Subject/Publisher) maintains a list of dependents (the Observers/Subscribers) and automatically notifies them of any state changes. It's a one-to-many relationship -- one subject, many observers.


Why do we need it?

Without the Observer Pattern, objects need to constantly "poll" (keep asking) for changes:

java
1// BAD EXAMPLE -- Polling is wasteful and fragile
2public class WeatherStation {
3    private double temperature;
4
5    public double getTemperature() { return temperature; }
6    public void setTemperature(double temp) { this.temperature = temp; }
7}
8
9// Every display has to constantly check for changes!
10public class PhoneDisplay {
11    private WeatherStation station;
12
13    public void checkForUpdates() {
14        // Are we there yet? Are we there yet? Are we there yet?
15        double temp = station.getTemperature();
16        System.out.println("Phone display: " + temp);
17        // But WHEN do we check? Every second? Every minute?
18        // What if we miss a change between checks?
19    }
20}
21
22// AND the weather station needs to know about EVERY display
23public class WeatherApp {
24    public static void main(String[] args) {
25        WeatherStation station = new WeatherStation();
26        PhoneDisplay phone = new PhoneDisplay();
27        TVDisplay tv = new TVDisplay();
28        WebDisplay web = new WebDisplay();
29
30        // We have to manually call each one. Forget one? Bug!
31        station.setTemperature(25.5);
32        phone.checkForUpdates();  // Manual!
33        tv.checkForUpdates();     // Manual!
34        web.checkForUpdates();    // Manual! What if we add a 4th display?
35    }
36}

The pain: Either observers constantly poll (wasting resources and possibly missing changes) or the subject has to manually notify each dependent (tight coupling). Adding a new observer means modifying the subject. Removing one means modifying it again. It's a mess.


How it works -- Step by Step

  1. Define the Observer interface -- Every observer must have an update() method that gets called when something changes.
  1. Define the Subject (Observable) interface -- It must support subscribe(), unsubscribe(), and notifyObservers().
  1. Implement the Subject -- It maintains a list of observers and loops through them when notifying.
  1. Implement concrete Observers -- Each one decides what to do when notified (update display, send email, log data, etc.).
  1. Wire them together -- Observers subscribe to the subject. When the subject's state changes, it calls notifyObservers(), which calls update() on every subscriber.

Let's Build It Together

Let's build a Sports Score Tracker -- a live scoreboard that notifies multiple displays when a goal is scored.

java
1// ========================================
2// Step 1: Define the Observer interface
3// Anyone who wants score updates must implement this
4// ========================================
5
6public interface ScoreObserver {
7    void update(String team1, String team2, int score1, int score2, String event);
8    String getObserverName();
9}
java
1// ========================================
2// Step 2: Define the Subject interface
3// The scoreboard must support subscribe/unsubscribe/notify
4// ========================================
5
6public interface ScoreSubject {
7    void subscribe(ScoreObserver observer);
8    void unsubscribe(ScoreObserver observer);
9    void notifyObservers();
10}
java
1// ========================================
2// Step 3: Implement the Subject -- the Live Match Scoreboard
3// ========================================
4
5public class LiveMatchScoreboard implements ScoreSubject {
6    private List<ScoreObserver> observers;  // The subscriber list
7    private String team1;
8    private String team2;
9    private int score1;
10    private int score2;
11    private String latestEvent;
12
13    public LiveMatchScoreboard(String team1, String team2) {
14        this.observers = new ArrayList<>();
15        this.team1 = team1;
16        this.team2 = team2;
17        this.score1 = 0;
18        this.score2 = 0;
19        System.out.println("Match created: " + team1 + " vs " + team2 + "\n");
20    }
21
22    @Override
23    public void subscribe(ScoreObserver observer) {
24        observers.add(observer);
25        System.out.println(observer.getObserverName() + " subscribed to match updates!");
26    }
27
28    @Override
29    public void unsubscribe(ScoreObserver observer) {
30        observers.remove(observer);
31        System.out.println(observer.getObserverName() + " unsubscribed from match updates.");
32    }
33
34    @Override
35    public void notifyObservers() {
36        System.out.println("  >> Notifying " + observers.size() + " observers...");
37        for (ScoreObserver observer : observers) {
38            observer.update(team1, team2, score1, score2, latestEvent);
39        }
40    }
41
42    // When a goal is scored, update state and notify everyone!
43    public void goalScored(int teamNumber) {
44        if (teamNumber == 1) {
45            score1++;
46            latestEvent = "GOAL! " + team1 + " scores!";
47        } else {
48            score2++;
49            latestEvent = "GOAL! " + team2 + " scores!";
50        }
51        System.out.println("\n" + latestEvent + " (" + score1 + " - " + score2 + ")");
52        notifyObservers();  // Automatically notify ALL subscribers
53    }
54
55    public void matchEvent(String event) {
56        this.latestEvent = event;
57        System.out.println("\n" + event);
58        notifyObservers();
59    }
60}
java
1// ========================================
2// Step 4: Implement concrete Observers
3// Each one reacts differently to score updates
4// ========================================
5
6// A big TV scoreboard display
7public class TVScoreDisplay implements ScoreObserver {
8    @Override
9    public void update(String team1, String team2, int score1, int score2, String event) {
10        System.out.println("    [TV DISPLAY]");
11        System.out.println("    +--------------------------+");
12        System.out.println("    | " + String.format("%-10s %2d - %-2d %10s", team1, score1, score2, team2) + " |");
13        System.out.println("    | " + event);
14        System.out.println("    +--------------------------+");
15    }
16
17    @Override
18    public String getObserverName() { return "TV Scoreboard"; }
19}
20
21// A mobile phone notification
22public class PhoneNotification implements ScoreObserver {
23    private String fanName;
24
25    public PhoneNotification(String fanName) {
26        this.fanName = fanName;
27    }
28
29    @Override
30    public void update(String team1, String team2, int score1, int score2, String event) {
31        System.out.println("    [PHONE -> " + fanName + "] "
32                         + event + " | Score: " + score1 + "-" + score2);
33    }
34
35    @Override
36    public String getObserverName() { return "Phone (" + fanName + ")"; }
37}
38
39// A statistics logger that records all events
40public class StatsLogger implements ScoreObserver {
41    private List<String> eventLog = new ArrayList<>();
42
43    @Override
44    public void update(String team1, String team2, int score1, int score2, String event) {
45        String logEntry = "[" + java.time.LocalTime.now().toString().substring(0, 8) + "] "
46                        + team1 + " " + score1 + " - " + score2 + " " + team2
47                        + " | " + event;
48        eventLog.add(logEntry);
49        System.out.println("    [STATS LOG] Recorded: " + event);
50    }
51
52    public void printFullLog() {
53        System.out.println("\n=== MATCH STATISTICS LOG ===");
54        for (String entry : eventLog) {
55            System.out.println("  " + entry);
56        }
57        System.out.println("============================\n");
58    }
59
60    @Override
61    public String getObserverName() { return "Stats Logger"; }
62}
63
64// A betting odds updater
65public class BettingOddsUpdater implements ScoreObserver {
66    @Override
67    public void update(String team1, String team2, int score1, int score2, String event) {
68        // Recalculate odds based on current score
69        double odds1 = score1 >= score2 ? 1.5 : 2.5;
70        double odds2 = score2 >= score1 ? 1.5 : 2.5;
71        System.out.println("    [BETTING] New odds -> " + team1 + ": " + odds1
72                         + " | " + team2 + ": " + odds2);
73    }
74
75    @Override
76    public String getObserverName() { return "Betting Odds"; }
77}
java
1// ========================================
2// Step 5: Run the match!
3// ========================================
4
5public class SportsApp {
6    public static void main(String[] args) {
7        // Create the match (Subject)
8        LiveMatchScoreboard match = new LiveMatchScoreboard("Eagles", "Tigers");
9
10        // Create observers
11        TVScoreDisplay tvDisplay = new TVScoreDisplay();
12        PhoneNotification alicePhone = new PhoneNotification("Alice");
13        PhoneNotification bobPhone = new PhoneNotification("Bob");
14        StatsLogger logger = new StatsLogger();
15        BettingOddsUpdater betting = new BettingOddsUpdater();
16
17        // Subscribe observers -- they'll be notified automatically
18        match.subscribe(tvDisplay);
19        match.subscribe(alicePhone);
20        match.subscribe(bobPhone);
21        match.subscribe(logger);
22        match.subscribe(betting);
23
24        // Match begins!
25        match.matchEvent("Kickoff! The match has begun!");
26
27        // Goals scored -- ALL observers notified automatically!
28        match.goalScored(1);  // Eagles score
29        match.goalScored(2);  // Tigers score
30        match.goalScored(1);  // Eagles score again
31
32        // Bob leaves the stadium (unsubscribes)
33        System.out.println();
34        match.unsubscribe(bobPhone);
35
36        // Another goal -- Bob won't be notified!
37        match.goalScored(2);  // Tigers score
38
39        // Print the stats log
40        logger.printFullLog();
41    }
42}

Aha moment: The LiveMatchScoreboard has NO idea what a TV display or phone notification does. It just calls update() on its list. You can add 100 new observer types without changing a single line in the scoreboard class. And observers can subscribe/unsubscribe dynamically!


Visual Mental Model

java
1  LiveMatchScoreboard (Subject)
2  +---------------------------+
3  | - observers: List         |        Subscribes
4  | - score1, score2          |    +-------------------+
5  |                           |    |                   |
6  | + subscribe(observer)     |    |   TVScoreDisplay  |----> Updates TV screen
7  | + unsubscribe(observer)   |    |   PhoneNotif(A)   |----> Sends push notif
8  | + notifyObservers() ------|----+   PhoneNotif(B)   |----> Sends push notif
9  |                           |    |   StatsLogger     |----> Logs to file
10  | + goalScored(team)        |    |   BettingOdds     |----> Recalculates odds
11  +---------------------------+    +-------------------+
12
13  goalScored() -> notifyObservers() -> loops through list -> calls update() on each
14
15  Observer joins:   subscribe(observer) adds to list
16  Observer leaves:  unsubscribe(observer) removes from list
17  Subject changes:  ALL remaining observers get update() called

Common Mistakes & Gotchas

  • Memory leaks from forgotten observers -- If an observer subscribes but never unsubscribes (and the subject lives forever), the observer can't be garbage collected. This is a classic Java memory leak. Always unsubscribe when done!
  • Order of notification -- Don't rely on observers being notified in a specific order. The subject just loops through the list, but that order shouldn't matter for correctness.
  • Cascading updates -- If Observer A's update() triggers a change that notifies observers again, you can get infinite loops. Be careful about observers that modify the subject.
  • Too much data in update() -- Passing too many parameters is messy. Consider passing the subject itself or a dedicated event object with all relevant data.
  • Push vs Pull -- In "push" style, the subject sends data to observers (like our example). In "pull" style, the subject just says "something changed" and observers query the subject for details. Pull is more flexible but requires observers to know about the subject.
  • Thread safety -- If observers subscribe/unsubscribe from different threads while notifications are happening, you need a CopyOnWriteArrayList or synchronized access.

Interview Tip

Observer Pattern is essential for event-driven systems in LLD interviews. It appears in: notification systems, stock price tickers, social media feeds, pub-sub messaging, and any "when X happens, notify Y" scenario. When you see "notify multiple things when something changes," immediately think Observer. In the interview, clearly distinguish between the Subject (has the data) and the Observers (react to changes). Draw the one-to-many relationship. Mention that it promotes loose coupling -- the subject and observers don't need to know about each other's internals.


Quick Quiz

  1. You're designing a stock market app. When a stock price changes, the portfolio screen, the alerts system, and the price chart all need to update. How would you model this with the Observer Pattern?
  1. What happens if notifyObservers() is called during an observer's update() method? How would you prevent infinite loops?
  1. In our Sports example, could we make observers filter which events they care about? (For example, Alice only wants goal notifications, not kickoff events.) How would you implement this?

Summary -- Key Takeaways

  • Observer Pattern creates a one-to-many dependency where one subject notifies many observers when its state changes.
  • Loose coupling is the main benefit -- the subject doesn't know what observers do, and observers can be added/removed dynamically.
  • Subscribe, unsubscribe, notify -- these three operations are the core mechanism that makes the pattern work.
  • Use it when multiple objects need to react to changes in another object, especially when the set of "interested parties" can change at runtime (notification systems, event handlers, data feeds, UI updates).