The Observer’s design pattern is used primarily when we are interested in any change of status being notified to anyone who needs to know about that change.
In the observer design pattern, the object that looks at the state of another object is called “Observer” and the object that is “looking” those changes is called “Subject”.
Java helps us by giving us tools to implement this pattern simply through the java.util.Observable class and the java.util.Observer interface.
Extending from java.util.Observable we define our class, which is capable of being observed by others.
Implementing the interface java.util.Observer we create our class to know if there are changes to the other Observable class.
What are the elements of the Observer design pattern?
We said that this pattern consists mainly of two parts: an observable functionality and another that needs to observe the changes of the first.
We have in this pattern:
- Subject: is the Observable class that will notify others when their status changes.
- Observer: is the class that will receive the notification. The class that wants to know about the changes.
How to create the Observer design pattern in Java
As we said previously, we need two parts. A class that allows to be observed and that will notify others that observe it.
Suppose we have a Bank class that when modified must generate certain expenses.
We want any change of status in the Bank class to notify the classes calculating the expenses of this change.
The Observable class is created by extending from java.util.Observable. Java will do everything for us with this class taking care to change the status of this and notify observers.
In this BankAccount class extending from Observable, we have the addAmount* method that receives an amount and then marks its status as changed and notifies all “observers” about the change made.
package patterns.observer;
import java.util.Observable;
public class BankAccount extends Observable {
private double balance;
public void addAmount(Double amount) {
this.balance += amount;
setChanged();
notifyObservers(balance);
}
public Double getBalance() {
return balance;
}
}
Then we have our class that implements the Observer interface and will receive the notification in the interface method update
This method update receives a value that we know is a double that comes from the BankAccount (Observable) class that notifies it.
Then with that value, you perform the corresponding calculations to calculate the expense.
package patterns.observer;
import java.util.Observable;
import java.util.Observer;
public class BankExpense implements Observer {
private String type;
private Double totalCalculated = new Double(0D);
private final Double rateCoefficient;
public BankExpense(String type, Double rateCoefficient) {
this.type = type;
this.rateCoefficient = rateCoefficient;
}
@Override
public void update(Observable o, Object value) {
this.totalCalculated = ((Double) value) * rateCoefficient;
}
public Double getTotalCalculated() {
return totalCalculated;
}
public String getType() {
return type;
}
@Override
public String toString() {
return "BankExpense{" +
"type='" + type + '\'' +
", rateCoefficient=" + rateCoefficient +
", totalCalculated=" + totalCalculated +
'}';
}
}
We now try the Observer pattern
We created the Observable object, BankAccount.
We created all the Observer objects, BankExpense, that need to know about the changes.
We add the Observer object to the Observable using addObserver*.
We change the status of the BankAccount object.
We see the result. All BankExpense objects were notified of the change.
package patterns.observer;
public class ObserverPatternExample {
public static void main(String[] args) {
// Observable class
BankAccount bankAccount = new BankAccount();
// Observer class
BankExpense bankExpense1 = new BankExpense("commission", 0.11d);
BankExpense bankExpense2 = new BankExpense("expense", 0.22d);
BankExpense bankExpense3 = new BankExpense("compensation", 0.33d);
// Add Observer into Observable
bankAccount.addObserver(bankExpense1);
bankAccount.addObserver(bankExpense2);
bankAccount.addObserver(bankExpense3);
// Change Observable state
bankAccount.addAmount(1000d);
// Observer was notified
System.out.println(bankExpense1.toString());
System.out.println(bankExpense2.toString());
System.out.println(bankExpense3.toString());
}
}
output
BankExpense{type='commission', rateCoefficient=0.11, totalCalculated=110.0}
BankExpense{type='expense', rateCoefficient=0.22, totalCalculated=220.0}
BankExpense{type='compensation', rateCoefficient=0.33, totalCalculated=330.0}
Some advantages and disadvantages of this pattern:
Using this pattern allows you to disengage the subjet (Observable) from all those observers (Observer).
Allows you to add an unlimited number of Observers who want to know about the change.
You have to be careful with the implementation of Observers because it could complicate the performance if there are many or if it is poorly implemented.
Conclusion
We have seen the Observer pattern and how easy it is to implement it, helping us in cases where we want other objects to be notified whenever a change occurs.
You can review this code GitHub