El patrón de diseño del Observador se usa principalmente cuando estamos interesados en que cualquier cambio de estado sea notificado a quien necesite enterarse de ese cambio.
En el patrón de diseño observer el objeto que mira el estado de otro objeto se llama “Observer” y el objeto que está “mirando” esos cambios se denomina “Subject”.
Java nos ayuda dándonos herramientas para implementar este patrón de forma simple a través de la clase java.util.Observable y la interfaz java.util.Observer.
Extendiendo de java.util.Observable definimos nuestra clase que es capaz de ser observada por otras.
Implementando la interfaz interfaz java.util.Observer creamos nuestra clase que conocer si hay cambios la otra clase Observable.
Cuales son las partes del patrón de diseño Observer
Dijimos que este patrón conforma principalmente de dos partes: una funcionalidad observable y otra que necesita observar los cambios de la primera.
Tenemos en este patrón:
- Subject: es la clase Observable que al cambiar su estado notificará a otras.
- Observer: es la clase que recibirá la notificación. La clase que quiere conocer sobre los cambios.
Como se crea el patrón de diseño Observer en Java
Tal como dijimos previamente necesitamos dos partes. Una clase que permite ser observada y que notificará a otras que la observan . Supongamos tenemos una clase Bank que al ser modificada debe generar ciertos gastos. Deseamos que cualquier cambio de estado en la clase Bank notifique de este cambio a las clases que calcula los gastos.
La clase Observable la creamos extendiendo de java.util.Observable. Java hará todo por nosotros con esta clase encargándose de cambiar el estado de esta y notificar a los observadores.
En esta clase BankAccount que extiende de Observable tenemos el método addAmount que recibe un monto y luego marca su estado como cambiado y notifica a todos los “observadores” sobre el cambio efectuado.
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;
}
}
Luego tenemos nuestra clase que implementa la interfaz Observer y que recibirá la notificación en el método de la interfaz update
Este método update recibe un value que sabemos es un double que viene de la clase BankAccount (Observable) que la notifica. Luego con ese valor realiza los cálculos que corresponde para calcular la expensa.
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 +
'}';
}
}
Probamos ahora el patrón Observer
Creamos el objeto Observable, BankAccount.
Creamos todas los objetos Observer, BankExpense, que necesitan enterarse de los cambios.
Agregamos el objeto Observer al Observable usando addObserver.
Cambiamos el estado de la del objeto BankAccount
Vemos el resultado. Todas los objetos BankExpense fueron notificados del cambio.
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());
}
}
La salida
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}
Algunas ventajas y desventajas de este patrón:
El uso de este patrón te permite desacoplar el subjet (Observable) de todos aquellos observadores (Observer) .
Permite agregar un número ilimitado de Observadores que desean enterarse del cambio.
Hay que tener cuidado con la implementación de los Observer porque podría complicar la performance si son muchos o si está mal implementada.
Conclusión
Hemos visto el patrón Observer y lo fácil que resulta su implementación ayudándonos en los casos en que deseamos que otros objetos sean notificados siempre que se produzca un cambio.
Puedes descargar este código en
github