Patrón de diseño Observer en Java

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.

Observer Pattern

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

Hi! If you find my posts helpful, please support me by inviting me for a coffee :)

Ver también