Observer (patrón de deseño)

Na Galipedia, a Wikipedia en galego.

O patrón observador é un patrón de deseño software que se encadra dentro da categoría de patróns de comportamento. Neste patrón, un obxecto, que chamaremos observado, mantén unha lista dos seus dependentes (dependencia un a moitos), chamados observadores, aos que notifica automáticamente de calquera cambio no seu estado.

Propósito[editar | editar a fonte]

Definir unha dependencia un-a-moitos entre obxectos de tal forma que cando o obxecto cambie o seu estado, todos os obxectos dependentes sexan notificados automáticamente.

Outros nomes[editar | editar a fonte]

  • Araña (Spider)
  • patrón de publicación-inscrición (Publish/Suscribe)
  • Modelo-patrón

Motivación[editar | editar a fonte]

O patrón xorde da necesidade de consistencia entre clases relacionadas pero mantendo un acoplamento baixo. Por exemplo, separar compoñente da interfaz gráfica dos obxectos que manteñen os datos da aplicación, para que sexan reutilizables independentemente.

O comportamento dos observadores depende do subxecto observado, que debería notificar calquera cambio no seu estado. Este pode ter un número ilimitado de observadores.

Aplicabilidade[editar | editar a fonte]

O patrón aplícase cando unha modificación no estado de un obxecto require cambios de outros e non se desexa coñecer cantos obxectos deben ser cambiados. Tamén é de aplicación no caso en que un obxecto debe ser capaz de notificar a outros obxectos sen facer ningunha suposición deles.

O patrón permitenos encapsular os dous aspectos da abstracción en obxectos separados polo que nos permite a súa variación e reutilización de modo independente.

Estrutura[editar | editar a fonte]

Diagrama de clases coa estrutura do patrón observador

Participantes[editar | editar a fonte]

SuxeitoObservado[editar | editar a fonte]

Coñece aos seus observadores. Proporciona unha interfaz para agregar e para eliminar observadores, así como para notificar aos seus observadores.

SuxeitoObservadoConcreto[editar | editar a fonte]

Mantén o estado de interese para os observadores concretos. Notifica aos seus observadores ao cambiar de estado.

Observador[editar | editar a fonte]

Define unha interfaz co método utilizado polo suxeito para notificar os cambios no seu estado.

ObservadorConcreto[editar | editar a fonte]

Mantén unha referencia ao suxeito concreto. Parte do seu estado debe permanecer consistente co estadio do suxeito ao que observa. Pon en funcionamento a interfaz de actualización.

Colaboracións entre participantes[editar | editar a fonte]

O suxeito concreto notifica aos seus observadores cada vez que se modifica o seu estado. Tras ser informado dun cambio no suxeito concreto, o observador concreto interroga ao suxeito concreto para modificar a percepción que ten do mesmo.

Diagrama de secuencia do patrón de deseño Observador

Consecuencias[editar | editar a fonte]

  • Abstrae o acoplamento entre suxeito e observador.
  • O suxeito non necesita especificar os observadores afectados por un cambio.
  • Descoñecemento das consecuencias de unha actualización.

Posta en funcionamento[editar | editar a fonte]

Posta en funcionamento suxeito-observadores. Observación de máis de un suxeito. Responsabilidade de inicio da notificación.

  • Suxeito invoca a notificación tras executarse un método que cambie o seu estado.
  • Clientes do suxeito (posiblemente os propios observadores) son responsables de comezar a notificación de actualición.

Referencias inválidas tras borrar un suxeito.

Asegurar a consistencia do estado do suxeito antes de iniciar a notificación.

Evitar a actualización dependente do observador.

  • Suxeito envía información detallada sobre o cambio (push model), esta solución é menos reusable.
  • O suxeito notifica o cambio e os observadores piden a información necesaria sobre o cambio (pull model). Esta solución é ineficiente, pero é máis reusable.

Especificación explicita das modificacións.

Código de exemplo (JAVA)[editar | editar a fonte]

JAVA ten propiamente unha posta en funcionamento para o patrón observador como moitas linguaxes

Clase java.util.Observable
 
   void addObserver(o Observer)
   protected void clearChanged() 
   int countObservers() 
   void deleteObserver(o Observer)
   void deleteObservers()
   boolean hasChanged() 
   void notifyObserver()
   void notifyObservers(Object data)
   protected void setChanged()


Interface java.util.Observer
   void update (Observable o, Object data)


Debaixo móstrase como debería ser dita posta en funcionamento sen percorrer as librerías propias da linguaxe JAVA.

Interfaz Observador[editar | editar a fonte]

/**
* Os observadores deben poñer en funcionamento a seguinte interfaz, é dicir,
* teñen un método actualizar() para reaccionar aos cambios do suxeito
*/
 
public interface Observador {
 
    public void actualizar();
 
}

Clase abstracta SuxeitoObservable[editar | editar a fonte]

/**
* Esta clase abstracta representa os obxectos susceptibles de ser
* observados. Incorpora métodos para engadir e eliminar observadores
* e un método de notificación. A asociación Observable-Observadores
* ponse en funcionamento mediante un vector de observadores
*/
 
import java.util.ArrayList;
 
public abstract class SuxeitoObservable {
 
 // o construtor crea o vector coa asociación Observable-Observador
 public Observable() {
     _observadores = new ArrayList<Observador>();
 }
 
 // engadir e eliminar sinxelamente operan sobre o vector _observadores...
 public void agregarObservador(Observador o) {
     _observadores.add(o);
 }
 
 public void eliminarObservador(Observador o) {
     _observadores.remove(o);
 }
 
// Notificación: Para cada observador invócase o método actualizar().
public void notificarObservadores() {
     for (Observador o:_observadores) {
             o.actualizar();
     }
}
 
// Este atributo privado mantén o vector cos observadores
 
private ArrayList<Observador> _observadores;
 
}

Clase Detector, ObservadorConcreto[editar | editar a fonte]

/**
* Observador moi simple que nin sequera consulta o estado do suxeito...
*/
 
public class Detector implements Observador {
 
public void actualizar() {
    System.out.println("Detector recibe actualizar!");
}
 
}

Clase SuxeitoObservable[editar | editar a fonte]

/**
* Exemplo de suxeito observable. É unha clase que mantén un valor enteiro no
* rango 0..máximo, onde o máximo se establece na construción. A clase
* dispón de métodos para modificar o valor e para acceder ao estado
* (valor, máximo). Estende a clase observable herdando o seu comportamento.
*/
 
public class Contador extends SuxeitoObservable {
 
// o construtor inicializa o estado do obxecto: o máximo e o
// valor inicial, sempre no rango 0..máximo. Adicionalmente,
// inicializa a parte de Observable que ten un Contador...
 
public Contador(int valor, int maximo) {
    super();
    _maximo = maximo;
    _valor = enRango(valor);
}
 
// este método privado asegura que un valor n esta entre 0..máximo
 
private int enRango(int n) {
    if (n < 0) {
 
return 0;
     } else if (n > _maximo) {
         return _maximo;
    } else {
 
return n;
    }
}
 
// Estes dous métodos permiten o acceso ao estado do contador
 
public int valor() {
    return _valor;
}
 
public int maximo() {
    return _maximo;
}
 
// Este método afecta ao estado: primeiro modifícase de forma consistente
// o estado e despois notifícase aos observadores do cambio
 
public void incrementarContador(int n) {
    _valor = enRango(_valor+n);
    notificarObservadores();
}
 
// atributos privados que manteñen o estado do contador
 
private int _valor, _maximo;
 
}

Clase Medidor, ObservadorConcreto[editar | editar a fonte]

/**
* Un exemplo de observador concreto da clase Contador.
*/
 
public class Medidor implements Observador {
 
// O construtor de Medidor establece a asociación entre Medidor-Contador
 
public Medidor(Contador contador) {
    _contador = contador;
}
 
// Tras ser notificado dun cambio, un observador Medidor accede
// ao estado do Contador utilizando a asociación
 
public void actualizar() {
    int porcentaxe = _contador.valor() * 100 / _contador.maximo();
    System.out.println("A porcentaxe do contador é " + porcentaxe + "%");
}
 
// mantén a asociación co contador
private Contador _contador;
}


Clase ValorContador, ObservadorConcreto[editar | editar a fonte]

/**
* Un exemplo de observador concreto da clase contador.
*/
 
public class ValorContador implements Observador {
 
// O construtor establece a asociación entre ValorContador-Contador
 
public ValorContador(Contador contador) {
    _contador = contador;
}
 
// Tras ser notificado dun cambio, un observador ValorContador accede
// ao estado do Contador utilizando a asociación
 
public void actualizar() {
    System.out.println("O valor do contador é " + _contador.valor() );
}
 
// mantén a asociación co suxeito observable
private Contador _contador;
 
}

Exemplo de uso, Clase Main[editar | editar a fonte]

public class Main {
 
 public static void main(String... argv) {
 
    //Xeramos un obxecto observable da clase Contador
    Contador c = new Contador(0,255);
 
    //Xeramos un obxecto observador e engadímolo como observador do contador
    Detector d = new Detector();
    c.agregarObservador(d);
    //Producimos unha variación no estado do contador
    c.incrementarContador(5);
 
    //Xeramos un obxecto observador e engadímolo como observador do contador
    ValorContador v = new ValorContador(c);
    c.agregarObservador(v);
    //Producimos unha variación no estado do contador
    c.incrementarContador(5);
 
    //Xeramos un obxecto observador e engadímolo como observador do contador
    Medidor m = new Medidor(c);
    c.agregarObservador(m);
    //Eliminamos o primeiro observador da lista de observadores do contador
    c.eliminarObservador(d);
    //Producimos unha variación no estado do contador
    c.incrementarContador(19);
}
 
}

Usos coñecidos[editar | editar a fonte]

  • Habitualmente úsase para poñer en funcionamento sistemas de manexadores de eventos.
  • Tamén é unha peza clave na arquitectura MVC (Model-View-Controller, Modelo-Vista-Controlador).

Patróns relacionados[editar | editar a fonte]

  • Mediador (Mediator)
  • Instancia única (Singleton)

Véxase tamén[editar | editar a fonte]

Bibliografía[editar | editar a fonte]

  • E. Gamma, R. Helm, R. Johnson, J. Vlissides. Design Patterns. Elements of Reusable Object-Oriented Software. Addison-Wesley Professional Computing Series.
  • M. Grand. Patterns in Java, a catalog of reusable design patterns illustrated with UML. Volume I. John Wiley & Sons.