Observer is one of the most popular design patterns. It has been used a lot on many software use cases and thus many languages out there provide it as a part of their standard library.
By using the observer pattern we can tackle the following challenges.
- Dependency with objects defined in a way that avoids tight coupling
- Changes on an object changes its dependent objects
- An object can notify all of its dependent objects
Imagine the scenario of a device with multiple sensors. Some parts of the code will need to get notified when new sensor data arrive and thus act accordingly. We will start by a simple class which represents the json data.
package com.gkatzioura.design.behavioural.observer;
public class SensorData {
private final String sensor;
private final Double measure;
public SensorData(String sensor, Double measure) {
this.sensor = sensor;
this.measure = measure;
}
public String getSensor() {
return sensor;
}
public Double getMeasure() {
return measure;
}
}
The we shall create the observer interface. Every class that implements the observer interface shall be notified once a new object is created.
package com.gkatzioura.design.behavioural.observer;
public interface Observer {
void update(SensorData sensorData);
}
Next step is to create the observable interface. The observable interface will have methods in order to register the observers that need to get notified.
package com.gkatzioura.design.behavioural.observer;
public interface Observable {
void register(Observer observer);
void unregister(Observer observer);
void updateObservers();
}
Now let us put some implementations.
The sensor listener will receive data from the sensors and notify the observers about the presence of data.
package com.gkatzioura.design.behavioural.observer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class SensorReceiver implements Observable {
private List data = new ArrayList();
private List observers = new ArrayList();
@Override
public void register(Observer observer) {
observers.add(observer);
}
@Override
public void unregister(Observer observer) {
observers.remove(observer);
}
public void addData(SensorData sensorData) {
data.add(sensorData);
}
@Override
public void updateObservers() {
/**
* The sensor receiver has retrieved some sensor data and thus it will notify the observer
* on the data it accumulated.
*/
Iterator iterator = data.iterator();
while (iterator.hasNext()) {
SensorData sensorData = iterator.next();
for(Observer observer:observers) {
observer.update(sensorData);
}
iterator.remove();
}
}
}
The we will create an observer which shall log the sensor data received to database, it might be an influxdb or an elastic search you name it.
package com.gkatzioura.design.behavioural.observer;
public class SensorLogger implements Observer {
@Override
public void update(SensorData sensorData) {
/**
* Persist data to the database
*/
System.out.println(String.format("Received sensor data %s: %f",sensorData.getSensor(),sensorData.getMeasure()));
}
}
Let’s put the all together.
package com.gkatzioura.design.behavioural.observer;
public class SensorMain {
public static void main(String[] args) {
SensorReceiver sensorReceiver = new SensorReceiver();
SensorLogger sensorLogger = new SensorLogger();
sensorReceiver.register(sensorLogger);
sensorReceiver.addData(new SensorData("temperature",1.2d));
sensorReceiver.updateObservers();
}
}
You can find the source code on github.