Patrón de diseño Observer en Java

Observer Pattern

El patrón de diseño Observer se utiliza principalmente en relaciones en donde estamos interesados en que cualquier cambio de estado sea notificado a quien lo necesite. 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. Alguien que se deja observar y alguien que quiere observar.

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 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 o en gitlab

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.