El patrón de diseño Observer (y su implementación en Spring)

Versión para imprimirVersión PDF

El patrón de diseño Observer esta clasificado como patrón de comportamiento y su objetivo es desacoplar objetos para aumentar la modularidad de nuestros componentes. La dependencia entre los objetos es definida en tiempo de ejecución y es especificada a través de un contrato en el cual se establece el canal de comunicación entre los objetos.

La idea central del patrón es que pueden existir "n" agentes externos que requieren conocer los diferentes estados de un componente durante su ciclo de vida, sin embargo, no pertenecen a él ni afectan o colaboran en su procesamiento, simplemente utilizan la información generada en sus diferentes estados. La solución propuesta en Observer, es proporcionar a nuestro componente una canal que permita la "suscripción" de observadores a través del cual podamos notificar a dichos obsevadores los diferentes cambios en el estado del componente.

A continuación el diagrama de clases del patrón.

En el anterior diagrama se observa el canal al que nos referimos, la interfaz IObservable proporciona las operaciones addObserver, removeObserver; que nos permiten establecer y/o deshechar dependencias en tiempo de ejecución. Además tenemos las operaciones de notificación notifyStateChange que implementan la comunicación entre los objetos de la dependencia.

A continuación la solución implementada en Java. Primero veremos la interfaz IObservable.

package org.yaxche.common.observer;

public interface IObservable<E> {

  /** Agrega el Observador especificado para ser notificado cuando se presente un
   *  cambio de estado en el Observable.
   */
  public void addObserver(IObserver aoObserver);

  /** Elimina el Observador especificado de la lista de notificaciones.
   */
  public void removeObserver(IObserver aoObserver);

  /** Notifica el cambio de estado a todos los obervadores registrados.
   */
  public void notifyStateChange();

  /** Notifica el cambio de estado a todos los obervadores registrados, especificando
   * el cambio.
   */
  public void notifyChange(E aoChange);

}

Podemos observar que en la interfaz Java echamos mano de los generics para permitir esteblecer más tarde en la implementación el tipo del cambio del estado.

Ahora veremos la interfaz IObserver.

package org.yaxche.common.observer;

public interface IObserver<E> {

  /** Recibe la notificación del cambio de estado en el IObservable.
   */
  public void update(IObservable aoTarget, E aoArg);
}

Al igual que la interfaz IObservable, en IObserver utilizamos generics con el mismo propósito, permitir al implementador establecer el tipo del cambio de estado.

Veamos ahora la implementación de las interfaces del patrón.

package org.yaxche.common.observer;

import java.util.LinkedHashSet;
import java.util.Set;

public class ConcreteObservable implements IObservable<String> {

  protected Set<IObserver> observers;
  protected String msStatus;
  
  /** LLeva a cabo la logica de la clase, aqui se va a modificar el estado del objeto
   * y se notifacara a los observadores
   */
  public void process() {
    this.msStatus = "Comienza procesamiento";
    this.notifyStateChange();
    ...
    this.notifyStateChange("Se actualizo el estado...");
  }

  @Override
  public void addObserver(IObserver aoObserver) {
    if(this.getObservers() != null) {
      this.getObservers().add(aoObserver);
    } else {
      this.setObservers(new LinkedHashSet<IObserver>());
      addObserver(aoObserver);
    }
  }

  @Override
  public void removeObserver(IObserver aoObserver) {
    if(this.getObservers() != null) {
      this.getObservers().remove(aoObserver);
    }
  }

  @Override
  public void notifyStateChange() {
    this.notifyChange(msStatus);
  }

  @Override
  public void notifyChange(String aoChange) {
    for(IObserver aoObs: this.getObservers()) {
      aoObs.update(this, aoChange);
    }
  }
}

En ConcreteObservable se observa una implentación de IObservable en donde se establece un java.util.Set para mantener la colección de IObservers; las operaciones notifyStateChange() y notifyStateChange(E) iteran dicho Set e invocan al método update() permitiendo así la actualización de estado en los observadores suscritos. Por otro lado la clase ConcreteObservable contiene su propia lógica en el método process, en dónde se originan los cambios de estado y por lo tanto se lanzan las notificaciones.

package org.yaxche.common.observer;

public class ConcreteObserver implements IObserver<String> {
  @Override
  public void update(IObservable aoTarget, String aoChange) {
    System.out.println(aoChange+": modificacion registrada en "+String.valueOf(aoTarget));
  }
}

Finalmente mostraremos como establecer la dependencia entre ambas clases a través del contexto de Spring.

  <!-- Se declara el observador como bean de Spring -->
  <bean id="concreteObserver" class="org.yaxche.common.observer.ConcreteObserver"></bean>
  
  <!-- Se declara el observable y se añade la dependencia del observador. -->
  <bean id="concreteObservable" class="org.yaxche.common.observer.ConcreteObservable">
    <property name="observers">
      <set>
        <ref bean="concreteObserver"></ref>
      </set>
    </property>
  </bean>

Como se puede observar el partón de diseño Observer es una herramienta muy útil para lograr una mejor modularización de nuestros componentes. Esta es una aproximación muy básica que puede ser mejorada (por ejemplo a través del uso de concurrencia), pero nos muestra el objetivo del patrón.

Your rating: None Average: 5 (5 votes)

Responder

CAPTCHA
Esta pregunta es para verificar qué eres un humano y prevenir envío de SPAM.
Image CAPTCHA
Enter the characters (without spaces) shown in the image.