Definition

It Defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. Just like the Subscription, as long as you take the subscription you will get the updates, when you invoke the Subscription you will stop getting the updates or say the services.


Real World Analogy

We are designing a Weather Application that consists of a WeatherData class. There are different types of displays attached to it, but currently, we only have CurrentConditionsDisplay. When the data inside the WeatherData class changes (i.e., temperature, pressure, and humidity), the updated data should be reflected on the display as well. The application should be designed in such a way that when a new display is added, we don’t need to rewrite or modify the existing code. The Observer Pattern is the ideal solution here. When the WeatherData class updates, it acts as a Subject, and its dependents (Observers/Subscribers) are notified automatically—as long as they are registered with the Subject.

Publisher Notifies all the Subscriber

Design

---
title: Observer Pattern
---
classDiagram
    direction TB

    class Subject {
        <<interface>>
        +registerObserver(o: Observer)
        +removeObserver(o: Observer)
        +notifyObserver()
    }

    class Observer {
        <<interface>>
        +update()
    }

    class DisplayElement {
        <<interface>>
        +display()
    }

    class WeatherData {
        - List<Observer> observers
        - float temperature
        - float humidity
        - float pressure
        +registerObserver(o: Observer)
        +removeObserver(o: Observer)
        +notifyObserver()
        +measurementsChanged()
        +setMeasurements(temperature: float, pressure: float, humidity: float)
        +getTemperature(): float
        +getHumidity(): float
        +getPressure(): float
    }

    class CurrentConditionDisplay {
        - WeatherData weatherdata
        +CurrentConditionDisplay(weatherdata: WeatherData)
        +display()
        +update()
    }

    class ForecastDisplay {
        - WeatherData weatherdata
        +ForecastDisplay(weatherdata: WeatherData)
        +display()
        +update()
    }

    Subject <|.. WeatherData
    Observer <|.. CurrentConditionDisplay
    Observer <|.. ForecastDisplay
    DisplayElement <|.. CurrentConditionDisplay
    DisplayElement <|.. ForecastDisplay

    WeatherData --> Observer : "maintains a list of"
    WeatherData --> CurrentConditionDisplay : "registers Observer"
    WeatherData --> ForecastDisplay : "registers Observer"

The Design will look like the Above.


Code in Java

// These is the Subject
interface Subject {
	public void registerObserver(Observer o);
	public void removeObserver(Observer o);
	public void notifyObserver();
}
 
interface Observer {
	public void update();
}
 
interface DisplayElement {
	public void display();
}
 
// These is the main subject the observer register here to receive the updates to all the subscribed observer.
class WeatherData implements Subject {
 
	private List<Observer> observers = new ArrayList<Observer>();
	private float temperature = 0;
	private float humidity = 0;
	private float pressure = 0;
 
	// Register the Observer
	@Override
	public void registerObserver(Observer o) {
		observers.add(o);
	}
 
	@Override
	public void removeObserver(Observer o) {
		observers.remove(o);
	}
 
	// Notify all the observer which are subscribed
	@Override
	public void notifyObserver() {
		for (Observer obs : observers) {
			obs.update();
		}
	}
 
	// when the measurements are changed notify's all the subscriber
	public void measurementsChanged() {
		this.notifyObserver();
	}
 
	// Sets the measurements and displays the updates.
	public void setMeasurements(float temperature, float pressure, float humidity) {
		this.humidity = humidity;
		this.pressure = pressure;
		this.temperature = temperature;
		this.measurementsChanged();
	}
 
	public float getTemperature() {
		return temperature;
	}
 
	public float getHumidity() {
		return humidity;
	}
 
	public float getPressure() {
		return pressure;
	}
 
}
 
// There can be multiple displays developer can add as many display as it wants by using DisplayElement Interface
class CurrentConditionDisplay implements Observer, DisplayElement {
	private WeatherData weatherdata;
 
	public CurrentConditionDisplay(WeatherData weatherdata) {
		this.weatherdata = weatherdata;
		this.weatherdata.registerObserver(this);
	}
 
	@Override
	public void display() {
		System.out.println("Current Condition = " + weatherdata.getHumidity() + " " + weatherdata.getPressure());
	}
 
	@Override
	public void update() {
		this.display();
	}
 
}
 
// lets add new display again
class ForecastDisplay implements DisplayElement, Observer {
	private WeatherData weatherdata;
 
	public ForecastDisplay(WeatherData weatherdata) {
		this.weatherdata = weatherdata;
		this.weatherdata.registerObserver(this);
	}
 
	@Override
	public void display() {
		System.out.println("Forecast Display = " + weatherdata.getHumidity() * 100 + " " + weatherdata.getPressure());
	}
 
	@Override
	public void update() {
		this.display();
	}
 
}
 
public class ObserverPattern {
 
	public static void main(String[] args) {
		// Creating the instance of Weather Class
		WeatherData weatherdata = new WeatherData();
 
		// Displaying the data from the Weather Data using Observer or Display
		CurrentConditionDisplay currcondition = new CurrentConditionDisplay(weatherdata);
		ForecastDisplay forecastdisplay = new ForecastDisplay(weatherdata);
 
		// Setting the measurements in the weather data
		weatherdata.setMeasurements(1, 1, 2);
		weatherdata.setMeasurements(5, 5, 5);
	}
}

Output:

 Current Condition = 2.0 1.0
 Forecast Display = 200.0 1.0
 Current Condition = 5.0 5.0
 Forecast Display = 500.0 5.0

Real World Example

The Observer Pattern is commonly used in many libraries and frameworks. For example, the Swing Library uses the Observer Pattern in the JButton class, which has various action listeners that are triggered when the button is clicked.

This class allows you to add or remove observers easily. You can also create multiple listeners by implementing the ActionListener interface.

Below is an example demonstrating this.

package observer;
 
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
 
// It is mostly used in Swing application 
class SwingApp {
	private JFrame jframe;
 
	public SwingApp() {
		jframe = new JFrame("Swing Example");
		jframe.setVisible(true);
		jframe.setBounds(new Rectangle(50, 50, 400, 400));
 
	}
 
	public void defineButton() {
		JButton button = new JButton("Click Me");
		button.setBounds(10, 10, 50, 30);
		jframe.add(button);
 
		// we are subscribing the observer when the button is clicked.
		button.addActionListener(new AngelListener());
	}
 
}
 
// These is the observer 
class AngelListener implements ActionListener {
 
	@Override
	public void actionPerformed(ActionEvent e) {
		System.out.println(e.getActionCommand());
	}
 
}
 
public class SwingApplication {
 
	public static void main(String[] args) {
		SwingApp app = new SwingApp();
		app.defineButton();
	}
 
}
 

Output:

When the button is clicked, the text “Click Me” appears in the console. When you click on the JButton, it notifies the observers subscribed to it that a click event has occurred, prompting them to execute their respective code.


Design Principles

  • Encapsulate What Varies - Identify the parts of the code that are going to change and encapsulate them into separate class just like the Strategy Pattern.
  • Favor Composition Over Inheritance - Instead of using inheritance on extending functionality, rather use composition by delegating behavior to other objects.
  • Program to Interface not Implementations - Write code that depends on Abstractions or Interfaces rather than Concrete Classes.
  • Strive for Loosely coupled design between objects that interact - When implementing a class, avoid tightly coupled classes. Instead, use loosely coupled objects by leveraging abstractions and interfaces. This approach ensures that the class does not heavily depend on other classes.