Observer design pattern
Observer pattern is used to separate the state of the object from its observers. There can be one object but many obesrvers. To maintain consistency, the observer pattern lets every observer update their information at a time. This is also called Publish and Subscribe.
There are 3 variants:
-
Notification and Pull: Notify the observers that the state is changed. The observers can decide whether to pull the latest state or not.
-
Notification and Push: Along with the notification, the latest state is also sent to the observer.
-
Periodic Pull: The observer will do a periodic pull of the latest state.
A simple example can be the state of a simulation that is logged and plotted:
class VehicleSim {
...
private double speed;
private GraphTool graphTool;
private LogTool logTool;
...
void updateState(double newSpeed) {
this.speed = newSpeed;
this.graphTool.updateSpeed(newSpeed);
this.logTool.updateSpeed(newSpeed);
}
...
}
class GraphTool {
...
@Override
void updateSpeed(double newSpeed) {
// Code to update the graph with the new speed.
}
...
}
class LogTool {
...
void updateSpeed(double newSpeed) {
// Code to update the log file with the new speed data.
}
...
}
At each speed update, the respective graph and log tools are called. In an extensive simulation class, such classes can be much more and the calls will also increase. To address this issue, we use observer pattern to model the visualization stuff.
A simple observer pattern consists of an Observer
interface and Subject
of interest whose state is being monitored. The Subject
can hold many instances of Observer
.
interface Observer {
void onUpdate(T);
}
abstract SimulationSubject { // Subject in this example.
...
private List<Observer> observers;
...
void addObserver(Observer<T> observer) {
this.observers.add(observer);
}
void removeObserver(Observer<T> observer) {
this.observers.remove(observer);
}
void notifyObservers() {
for (Observer<T> observer: this.observers) {
this.observer.onUpdate(T);
}
}
...
}
The graph and log tools are modified to implement the Observer
interface.
class GraphTool implements Observer {
...
@Override
void onUpdate(double newSpeed) {
// Code to update the graph with the new speed.
}
...
}
class LogTool implements Observer {
...
@Override
void onUpdate(double newSpeed) {
// Code to update the log file with the new speed data.
}
...
}
The VehicleSim
will now become our Subject by extending the SimulationObject
class.
class VehicleSim extends SimulationObject {
...
private double speed;
...
void updateState(double newSpeed) {
this.speed = newSpeed;
this.observers.notifyObservers(speed);
}
...
}
Whenever the updateState()
method is called in the VehicleSim
, all the observers are notified of the speed update. They can use the latest information to perform their respective tasks. By having this observer as an abstract class, we reduce cluttering even in the VehicleSim
class. Moreover, this also reduces instances of tools getting outdated data.
Enjoy Reading This Article?
Here are some more articles you might like to read next: