Introduction
In the previous post on the Observer Design Pattern, we learnt what an Observer design pattern is and when and how to use it. For the implementation, we created our own Observable (Subject) and Observer interfaces and implemented them. In fact, Java provides Observable and Observer classes or interfaces that we can use rather than creating our own.
However, it is not as simple as just replacing our interfaces with that provided by Java library. We need to know certain things which we will see in this post. In summary, we will implement the Observer Pattern using Java’s Observable class (and Observer). We will use same usecase from the previous post and hence I request you to have to look at that first before moving on.
A quick summary
We have a Subject holding the StockDetails and observers register themselves with the Subject to get notifications whenever the Stock details change. The Subject interface had three methods:
- registerObserver - called by an Observable to register with the Subject
- removeObserver - called by an Observable when it no longer wants to be registerd and
- notifyObserver - called by the Observer when it has to send notifications to all its observers
The Observer had a single upadate method that the Subject/Observable called to push the updated stock price value to the observers.
Java’s Observable class
Java provides support for Observable as an abstract class and not an interface (Observable). It has the following methods
- addObserver(Observer o)
- deleteObserver(Observer o)
- notifyObservers()
These are very similar to the methods in the Subject interface we created when implementing Observer pattern in the last post. There is another important method called setChanged which I’ll explain in a moment.
Java’s Observer class
If we look at the Observer interface, the update method has the updated stock values as parameters. This indicates that the Observer interface is not a generic contract. Rather it is tied to our StockData use-case. This model of passing the updates values from the Observable to the Observer is the push model. There is another model where the Observer, when notified by the Observable, pulls the values it needs. This is useful when not all Observers need all the values. This implies that the Observable needs to have accessor or getter methods to expose the state details (in our case it is the StockData’s instance variables).
Java provides an interface for Observer contract (java.util.Observer). It has a single method update with the below signature.
void update(Observable o, Object arg)
This would be called by the Observable when it has to update the Observer.
The first parameter is the Observable object that is sending the notification. The second object is the data that is sent by the Observable. Note that this can be null.
When arg is not-null, we are doing the push model. When it is null, the observers has to call the getter or accessor methods on the Observable (like o.getSomeValue()) on the Observable to pull in the new values.
Creating an Observer
For a class to be an Observer, it has to implement the java.util.Observer interface and implement the update method. To add itself as an Observer, call the addObserver method on an Observerable passing itself as the argument.
Creating an Observable
Extend the java.util.Observable abstract class. Then, before sending the notification, we must first call the setChanged method. Then call either notifyObservers() or notifyObservers(Object arg).
Use the first version, if the Observer has to pull the data. To pass the data, use the second method. In this case, the Observable will pass the data to the update method’s second parameter.
Why call setChanged method
If the observable calls notifyObservers without calling setChanged, the observers will not be notified. The logic of notifyObserver is like
setChanged() {
changed = true;
}
notifyObservers(Object arg) {
if (changed) {
for each observer on the list {
call update(this, arg)
}
}
changed = false;
}
notifyObservers() {
notifyObservers(null);
}
The use of setChanged is to control when to send notifications. Say for some use-cases the values will be erratic and can change very frequently. We might want to notify the observers only when the values stabilize or change more than a certain threshold. When fluctuating changes happen, we can update the values on the Observable but not call setChanged. This will prevent in notifying the observers. When the values stabilize finally, we can call setChanged and call notifyObservers.
Implementing Observer Pattern using Java’s Observable class
import java.util.Observable;
public class StockData extends Observable {
private String stockSymbol;
private String stockPrice;
public String getStockSymbol() {
return stockSymbol;
}
public String getStockPrice() {
return stockPrice;
}
public void stockPriceChanged() {
setChanged();
notifyObservers();
}
public void setNewStockPrice(String stockSymbol, String stockPrice) {
this.stockSymbol = stockSymbol;
this.stockPrice = stockPrice;
stockPriceChanged();
}
}
import java.util.Observable;
import java.util.Observer;
public class CurrentStockDetailsDisplay implements Observer, DisplayElement {
private Observable observable;
private String stockSymbol;
private String stockPrice;
public CurrentStockDetailsDisplay(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
@Override
public void display() {
System.out.println("Stock Symbol - " + stockSymbol + " " + "Stock price - " + stockPrice);
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof StockData) {
StockData stockData = (StockData) o;
this.stockSymbol = stockData.getStockSymbol();
this.stockPrice = stockData.getStockPrice();
display();
}
}
}
public class Main {
public static void main(String[] args) {
StockData stockData = new StockData();
CurrentStockDetailsDisplay currentStockDetailsDisplay = new CurrentStockDetailsDisplay(stockData);
stockData.setNewStockPrice("StockA", "10.5");
stockData.setNewStockPrice("StockA", "11.5");
}
}
Outputs:
Stock Symbol - StockA Stock price - 10.5
Stock Symbol - StockA Stock price - 11.5
Advantages of using built-in Observer class
By using the Observable class, the logic to add, remove and notify observers are taken care in the abstract class and we don’t have to implement it.
The Observer contract we use now is a generic one and can be used for any usecase.
Disadvantages of using built-in Observer class
There are some disadvantages of using the Java’s built-in Observer class in implementing the Observer design pattern.
- Observable is a class. So, to implement an Observable we have to subclass it. Since there is no multiple inheritance in Java, we cannot add Observable behavior to an existing class that subclasses some other class.
- The setChanged method is protected. This means that to call this we have to subclass Observable. If we have composed Observable, we cannot call this method. This violates the principle favour composition over inheritance.
Conclusion
This post is an extension from the previous post on the Observer design pattern. First, we looked at the Java’s Observable class and Observer interface and their contracts. Second, we learnt what are to be done to create Observables and Observers using these. Third, we looked at the code implementing this. Finally, we saw the pros and cons of using Java’s built-in Observerable class.