Last Updated: 27.02.2026
The Observer Design Pattern is a behavioral pattern that defines a one-to-many dependency between objects so that when one object (the subject) changes its state, all its dependents (observers) are automatically notified and updated.
This pattern is useful in scenarios where:
- You have multiple parts of the system that need to react to a change in one central component.
- You want to decouple the publisher of data from the subscribers who react to it.
- You need a dynamic, event-driven communication model without hardcoding who is listening to whom.
It is also known as Publish–Subscribe Pattern.
When you subscribe to a YouTube channel:
- You = Observer
- Channel = Subject
- When a new video is uploaded → All subscribers get notified.
Subscribers don’t constantly check. They get notified automatically.
That is Observer Pattern.
Use it when:
- One object change must notify many objects.
- You need event-driven architecture.
- You want loose coupling.
- You want dynamic subscription/unsubscription.
Imagine we are building a stock price notification system, Whenever a stock price changes, we need to notify:
📧 Email subscribers
📱 Mobile app users
📩 SMS subscribers
A naive implementation might look like this:
public class StockService {
private EmailService emailService;
private SmsService smsService;
private MobileAppService mobileAppService;
public void setStockPrice(double price) {
// Update stock price internally...
if (emailService != null) {
emailService.send(price);
}
if (smsService != null) {
smsService.send(price);
}
if (mobileAppService != null) {
mobileAppService.push(price);
}
}
}🔗 Tight Coupling: StockService directly depends on concrete notification services.
❌ Violates Open/Closed Principle (OCP): If we add WhatsApp notifications, we must modify StockService.
🧱 Hard to Extend: Every new subscriber type requires changes in existing code.
🧪 Difficult to Test: Notification logic is mixed with business logic.
📉 Poor Scalability: As subscribers grow, the method becomes messy and unmaintainable.
We decouple the notification mechanism from the stock price logic.
🎯 Key Idea
Stock price system becomes the Subject
Subscribers (Email, SMS, Mobile App) become Observers
Observers register themselves
Subject notifies all observers automatically when price changes
| 🏷 Component | 🎯 Role | 💡 Responsibility |
|---|---|---|
| 📢 Subject (Observable) | Maintains state | Keeps list of observers and notifies them on change |
| 🔔 Observer | Receives updates | Defines update contract |
| 🏗 Concrete Subject | Implements Subject | Stores actual state and triggers notifications |
| 📧 Concrete Observer | Implements Observer | Performs action when notified |
| 👤 Client | Configures system | Registers/unregisters observers |
🎯 Easy Way to Remember (Interview Trick)
📢 Subject → “I have data.”
🔔 Observer → “Notify me when it changes.”
🏗 Concrete Subject → “State changed! Notifying everyone.”
📧 Concrete Observer → “Got the update. Taking action.”
👤 Client → “You subscribe. You unsubscribe.”
com.designpatterns.observer
│
├── observer
│ ├── Observer.java
│ ├── EmailObserver.java
│ ├── MobileAppObserver.java
│ └── SmsObserver.java
│
├── subject
│ ├── Subject.java
│ └── StockPriceSubject.java
│
└── Application.java
package com.designpatterns.observer.observer;
public interface Observer {
public void update(double stockPrice);
}📁 EmailObserver.java
package com.designpatterns.observer.observer;
public class EmailObserver implements Observer {
private final String email;
public EmailObserver(String email) {
this.email = email;
}
@Override
public void update(double stockPrice) {
System.out.println("Email sent to " + email
+ " with updated stock price: ₹" + stockPrice);
}
}📁 MobileAppObserver.java
package com.designpatterns.observer.observer;
public class MobileAppObserver implements Observer {
private final String username;
public MobileAppObserver(String username) {
this.username = username;
}
@Override
public void update(double stockPrice) {
System.out.println("Push notification sent to user: "
+ username + " | New Price: ₹" + stockPrice);
}
}📁 SmsObserver.java
package com.designpatterns.observer.observer;
public class SmsObserver implements Observer {
private final String phoneNumber;
public SmsObserver(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
@Override
public void update(double stockPrice) {
System.out.println("SMS sent to " + phoneNumber +
" | Updated Price: ₹" + stockPrice);
}
}📁 Subject.java
package com.designpatterns.observer.subject;
import com.designpatterns.observer.observer.Observer;
public interface Subject {
public void registerObserver(Observer observer);
public void removeObserver(Observer observer);
public void notifyObservers();
}📁 StockPriceSubject.java
package com.designpatterns.observer.subject;
import com.designpatterns.observer.observer.Observer;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class StockPriceSubject implements Subject {
private final List<Observer> observers = new ArrayList<>();
private double stockPrice;
@Override
public void registerObserver(Observer observer) {
observers.add(Objects.requireNonNull(observer));
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(stockPrice);
}
}
public void setStockPrice(double stockPrice) {
this.stockPrice = stockPrice;
notifyObservers();
}
}📁 Application.java
package com.designpatterns.observer;
import com.designpatterns.observer.observer.EmailObserver;
import com.designpatterns.observer.observer.MobileAppObserver;
import com.designpatterns.observer.observer.SmsObserver;
import com.designpatterns.observer.subject.StockPriceSubject;
public class ObserverApplication {
public static void main(String[] args) {
StockPriceSubject stockPriceSubject = new StockPriceSubject();
// Register observers
stockPriceSubject.registerObserver(new EmailObserver("ripan@gmail.com"));
stockPriceSubject.registerObserver(new MobileAppObserver("ripan_user"));
stockPriceSubject.registerObserver(new SmsObserver("1234567890"));
// Change state
stockPriceSubject.setStockPrice(1500.00);
System.out.println("----- Price Updated Again -----");
stockPriceSubject.setStockPrice(2000.00);
}
}Email sent to ripan@gmail.com with updated stock price: ₹1500.0
Push notification sent to user: ripan_user | New Price: ₹1500.0
SMS sent to 1234567890 | Updated Price: ₹1500.0
----- Price Updated Again -----
Email sent to ripan@gmail.com with updated stock price: ₹2000.0
Push notification sent to user: ripan_user | New Price: ₹2000.0
SMS sent to 1234567890 | Updated Price: ₹2000.0
| Role | In Our Example |
|---|---|
| Subject | StockPriceSubject |
| Observer | Observer |
| Concrete Observer | EmailObserver, MobileAppObserver, SmsObserver |
| Client | Application |
Push Model: Subject pushes full data to observers (we used this).
Pull Model: Observer asks subject for data after notification.
Interviewers LOVE this difference.
| 🌟 Advantage | 💡 Explanation |
|---|---|
| 🔗 Loose coupling | Subject doesn’t know concrete observer |
| 📢 Automatic notification | State changes propagate automatically |
| ➕ Easy to add new observers | No modification in subject |
| 🧩 Follows Open/Closed Principle | Extensible design |
| ⚡ Great for event systems | Reactive architecture |
| ⚠ Drawback | 💡 Explanation |
|---|---|
| 🧠 Can cause memory leaks | If observers not removed properly |
| 🔄 Notification chain complexity | Hard to debug large systems |
| 🐢 Performance issue | Too many observers can slow system |
| ⚠ Unexpected updates | Observers may receive frequent updates |
- Java
EventListener - Swing event model
- Spring Application Events
- Kafka consumers
- React state updates
Observer Pattern establishes a one-to-many relationship between objects so that when one object changes state, all its dependents are notified automatically. It enables loose coupling and event-driven design.
