The Observer design pattern is a behavioral design pattern that allows one-to-many communication between objects. In this pattern, an object called the subject maintains a list of its dependents, called observers, and notifies them automatically of any changes in its state.
In this article, we will explore the Observer design pattern and how it works in Java, with a sample code.
What is an Observer Design Pattern?
The Observer pattern is useful when you need to keep multiple objects in sync with the state of another object. For example, in a weather application, you may want to notify users whenever the temperature changes. In this case, the temperature object is the subject, and the users are the observers.
The Observer pattern consists of the following components:
Subject: This is the object that has a list of observers and notifies them of any changes in its state.
Observer: This is the interface that defines the method(s) that the subject calls to notify the observers of any changes in its state.
ConcreteSubject: This is the class that implements the Subject interface and maintains a list of observers.
ConcreteObserver: This is the class that implements the Observer interface and receives notifications from the subject.
Why use the Observer Design Pattern?
The Observer Design Pattern is a tool for building loosely coupled systems that can be easily extended and maintained. Here are some of the key benefits of using the Observer Design Pattern:
Decoupling: The Observer pattern promotes decoupling between objects by allowing them to interact through an interface, rather than directly. This reduces the dependencies between the objects and makes the system more flexible.
Flexibility: Since the Observer pattern uses interfaces to define the interactions between objects, it allows for a wide range of implementation possibilities. This makes it easier to change or extend the system without affecting other parts of the code.
Reusability: The Observer pattern promotes reusability by allowing the same observer to be used with multiple subjects. This reduces the amount of code duplication and makes the code easier to maintain.
Extensibility: The Observer pattern makes it easy to add new observers and subjects to the system. This allows the system to grow and adapt to changing requirements over time
Observer Design Pattern with Java
To implement the Observer pattern in Java, we need to define two interfaces - the Subject and the Observer. The Subject interface defines the methods to register, remove and notify observers. The Observer interface defines the method that is called by the Subject when there is a change in its state.
Let's consider a simple example where we have a Weather Station that collects temperature and humidity readings and displays them on multiple devices. In this scenario, the Weather Station is the Subject, and the devices that display the readings are Observers.
We will start by defining the Observer interface as follows:
public interface Observer {
void update(float temperature, float humidity);
}
This interface defines the update() method that will be called by the Subject when there is a change in its state. The method takes two arguments - the temperature and humidity readings.
Next, we define the Subject interface as follows:
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
This interface defines the methods to register, remove and notify observers. The registerObserver() method is used to add a new Observer to the list of observers. The removeObserver() method is used to remove an Observer from the list of observers. The notifyObservers() method is used to notify all registered observers when there is a change in the state of the Subject.
Now, we can implement the Weather Station as the Subject:
import java.util.ArrayList;
public class WeatherStation implements Subject {
private ArrayList<Observer> observers;
private float temperature;
private float humidity;
public WeatherStation() {
observers = new ArrayList<Observer>();
}
public void registerObserver(Observer observer) {
observers.add(observer);
}
public void removeObserver(Observer observer) {
int index = observers.indexOf(observer);
if (index >= 0) {
observers.remove(index);
}
}
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(temperature, humidity);
}
}
public void setMeasurements(float temperature, float humidity) {
this.temperature = temperature;
this.humidity = humidity;
measurementsChanged();
}
public void measurementsChanged() {
notifyObservers();
}
}
In this implementation, we have defined an ArrayList to store the Observers. The registerObserver() method adds a new Observer to the ArrayList, and the removeObserver() method removes an Observer from the ArrayList. The notifyObservers() method iterates through the ArrayList and calls the update() method on each Observer.
We have also defined the setMeasurements() method to update the temperature and humidity readings, and the measurementsChanged() method to notify the Observers when there is a change in the state of the Weather Station.
Finally, we can implement a device that displays the temperature and humidity readings as an Observer:
public class DisplayDevice implements Observer {
private float temperature;
private float humidity;
public void update(float temperature, float humidity) {
this.temperature = temperature;
this.humidity = humidity;
display();
}
public void display() {
System.out.println("Temperature: " + temperature + " Humidity: " + humidity);
}
}
In this implementation, the update() method is called by the Weather Station when there is a change in its state. The method updates the temperature and humidity readings and calls the display() method to display the readings on the device.
Conclusion
The Observer pattern is a useful way to implement the publish-subscribe model, where multiple objects need to be notified of changes to a single object. It promotes loose coupling between objects and can simplify code by separating the subject and observer logic. It is particularly useful in situations where there are many objects that need to be updated when a particular event occurs. By using this pattern, you can create a system that is easy to extend, maintain, and scale.
Comments