viernes, 3 de febrero de 2017

Patrón Observador




Hemos vuelto, esta vez escribo sobre el patrón observador (the observer pattern), este patrón es muy simple y se usa cundo se require que alguien se entere de que algo ha sucedido dentro de otro ámbito, de allí su nombre, veremos un ejemplo sencillo:

Lo primero que se requiere es una interface Observer de quien saldrán las implementaciones que serán los Observadores:

public interface Observer {
    void update();

}

Luego tendremos otra interface Observable que es la que va a implementar nuestra objeto que necesitamos que notifique sus cambios de estado:

public interface Observable {
    public void addObserver(Observer observer);
    public void removeObserver(Observer observer);
    public void notifyObservers();

}

Ahora las implementaciones, primero veamos la implementación de Observable:

import java.util.ArrayList;
import java.util.List;

public class Invoice implements Observable {

    
    private String serialNumber;
    
    private String status;
    
    public Invoice(final String serialNumber) {
        this.serialNumber = serialNumber;
        this.status = "new";
    }
    
    public String getSerialNumber() {
        return serialNumber;
    }
    
    public String getStatus() {
        return status;
    }
    
    private List<Observer> observers = new ArrayList<Observer>();
    
    public void markAsPaid() {
        notifyObservers();
    }
    
    public void addObserver(final Observer observer) {
        this.observers.add(observer);
    }

    public void removeObserver(final Observer observer) {
        this.observers.remove(observer);
    }

    public void notifyObservers() {
        for(Observer observer : this.observers) {
            observer.update();
        }
    }


}

Por último las implementaciones de la interfaz Observer:

public class InvoiceStorer implements Observer {

    private Invoice invoice;
    
    public InvoiceStorer(final Invoice invoice) {
        this.invoice = invoice;
        this.invoice.addObserver(this);
    }
    
    public void update() {
        System.out.println("Guardando cambio de estado de la factura: " 
                + this.invoice.getSerialNumber() 
                + " a " + this.invoice.getStatus());
    }
    
    public String getInvoiceSerialNumber() {
        return invoice.getSerialNumber();
    }

}

public class InvoiceStatusChangeNotifyer implements Observer {

    private Invoice invoice;
    
    public InvoiceStatusChangeNotifyer(final Invoice invoice) {
        this.invoice = invoice;
        this.invoice.addObserver(this);
    }
    
    public void update() {
        System.out.println("Notificar pago de factura: " + invoice.getSerialNumber());
    }
    
    public String getInvoiceSerialNumber() {
        return invoice.getSerialNumber();
    }

}

Tenemos un test para probar la funcionalidad hecha:

import java.util.UUID;

import org.junit.Assert;
import org.junit.Test;

public class InvoiceTest {
    
    @Test
    public void markAsPaid(){
        String serialNumber = UUID.randomUUID().toString();
        Invoice invoice = new Invoice(serialNumber);
        InvoiceStorer storer = new InvoiceStorer(invoice);
        InvoiceStatusChangeNotifyer notifyer = new InvoiceStatusChangeNotifyer(invoice);
        invoice.markAsPaid();
        Assert.assertEquals(serialNumber, storer.getInvoiceSerialNumber());
        Assert.assertEquals(serialNumber, notifyer.getInvoiceSerialNumber());
    }

}
La salida del test es:

Guardando cambio de estado de la factura: db1a5606-2aad-475c-b4af-bf59f03975d4 a new

Notificar pago de factura: db1a5606-2aad-475c-b4af-bf59f03975d4

Ahora si vamos a ver lo que sucedió.

Lo primero es que los Observers al inicializarse se "registran" haciendo la llamada al método addObserver de la clase Invoice, este método llena una lista de observadores a la que al momento de que se actualiza su estado (método markAsPaid) les notifica del cambio hecho.

Cuando los observadores son notificados hacen uso de la instancia de su observable para hacer su trabajo, por ejemplo el InvoiceStorer se encargará de guardar el cambio de estado en la base de datos, mientras que el InvoiceStatusChangeNotifyer se encargará de enviar una notificación por correo electrónico informando del pago de la factura.

Como vemos, esto se pudo haber hecho en sólo el método de markAsPaid pero usando el patrón observador se puede hacer una mejor separación de responsabilidades.

No hay comentarios.:

Publicar un comentario