A common side-effect of partitioning a system into a collection of cooperating classes is the need to maintain consistency between related objects. You don’t want to achieve consistency by making the classes tightly coupled, because that reduces their reusability. Observer pattern tries to manage the flow of information between different parties, namely subject and observers.

1. Intent

Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

2. Also Known As

Dependents, Publish-Subscribe

3. Applicability

Use the Observer pattern in any of the following situations:

  • When an abstraction has two aspects, one dependent on the other. Encapsulating these aspects in separate objects lets you vary and reuse them independently.
  • When a change to one object requires changing others, and you don’t know how many objects need to be changed.
  • When an object should be able to notify other objects without making assumptions about who these objects are. In other words, you don’t want these objects tightly coupled.

4. How To Implement

In observer pattern, there is one subject and multiple observers. We will start with observers. To detail the example implementation, we will use weather station abstraction.

4.1. Observer

public interface Observer {

    void update(float temp, float humidity, float pressure);
}

 

ForecastDisplay and StatisticsDisplay are some implementations. They use incoming weather data to update their displays.

public class ForecastDisplay implements Observer {

    public void update(float temp, float humidity, float pressure) {
        // Process the change
        System.out.println(String.format("%f\t%f\t%f", temp, humidity, pressure));
    }
}
public class StatisticsDisplay implements Observer {

    public void update(float temp, float humidity, float pressure) {
        // Process the change
        System.out.println(String.format("%f\t%f\t%f", temp, humidity, pressure));
    }
}

 

4.2. Subject

Subject interface contains the methods to manage and notify observers. As you can see, registerObserver() and removeObserver() methods provide observer management functionality. And notifyObservers() handles the job of notifying observers about state changes.

public interface Subject {

    void registerObserver(Observer observer);

    void removeObserver(Observer observer);

    void notifyObservers();
}

 

WeatherStation is the default implementation. When weather values change by the invocation of setMeasurements() method, it notifies registered observers.

public class WeatherStation implements Subject {

    private final List<Observer> observers;
    private float temp;
    private float humidity;
    private float pressure;

    public WeatherStation() {
        observers = new ArrayList<>();
    }

    public void registerObserver(Observer observer) {
        if (!observers.contains(observer)) {
            this.observers.add(observer);
        }
    }

    public void removeObserver(Observer observer) {
        this.observers.remove(observer);
    }

    public void notifyObservers() {
        for (Observer o : observers) {
            o.update(temp, humidity, pressure);
        }
    }

    public void measurementsChanged() {
        notifyObservers();
    }

    public void setMeasurements(float temp, float humidity, float pressure) {
        this.temp = temp;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }
}

4.3. Sample Invocation

public class ClientMain {

    public static void main(String[] args) {
        WeatherStation weatherStation = new WeatherStation();
        
        CurrentConditions currentConditions = new CurrentConditions();
        ForecastDisplay forecastDisplay = new ForecastDisplay();
        StatisticsDisplay statisticsDisplay = new StatisticsDisplay();
        StatisticsDisplay secondStaticsDisplay = new StatisticsDisplay();

        weatherStation.registerObserver(currentConditions);
        weatherStation.registerObserver(forecastDisplay);
        weatherStation.registerObserver(statisticsDisplay);
        weatherStation.removeObserver(secondStaticsDisplay);

        Random random = new Random();
        for (int i = 0; i < 3; i++) {
            weatherStation.setMeasurements(random.nextFloat(), random.nextFloat(), random.nextFloat());
            System.out.println("***************************************");
        }
    }
}

Output:

0.567413	0.376702	0.871033
0.567413	0.376702	0.871033
0.567413	0.376702	0.871033
***************************************
0.184101	0.862047	0.626799
0.184101	0.862047	0.626799
0.184101	0.862047	0.626799
***************************************
0.831630	0.087314	0.384768
0.831630	0.087314	0.384768
0.831630	0.087314	0.384768
***************************************

5. Source Code

Source code can be found on Github.

Leave a Reply

Close Menu