El patrón de observador en Java

1. Información general

En este artículo, describiremos el patrón Observer y veremos algunas alternativas de implementación de Java.

2. ¿Qué es el patrón de observador?

El observador es un patrón de diseño de comportamiento. Especifica la comunicación entre objetos: observables y observadores . Un observable es un objeto que notifica a los observadores sobre los cambios en su estado.

Por ejemplo, una agencia de noticias puede notificar a los canales cuando recibe noticias. Recibir noticias es lo que cambia el estado de la agencia de noticias y hace que los canales sean notificados.

Veamos cómo podemos implementarlo nosotros mismos.

Primero, definamos la clase NewsAgency :

public class NewsAgency { private String news; private List channels = new ArrayList(); public void addObserver(Channel channel) { this.channels.add(channel); } public void removeObserver(Channel channel) { this.channels.remove(channel); } public void setNews(String news) { this.news = news; for (Channel channel : this.channels) { channel.update(this.news); } } }

NewsAgency es un elemento observable, y cuando las noticias se actualizan, el estado de NewsAgency cambia. Cuando ocurre el cambio, NewsAgency notifica a los observadores sobre este hecho llamando a su método update () .

Para poder hacer eso, el objeto observable necesita mantener referencias a los observadores , y en nuestro caso, es la variable de canales .

Veamos ahora cómo puede verse el observador , la clase Canal . Debe tener el método update () que se invoca cuando cambia el estado de NewsAgency :

public class NewsChannel implements Channel { private String news; @Override public void update(Object news) { this.setNews((String) news); } }

La interfaz del canal tiene un solo método:

public interface Channel { public void update(Object o); }

Ahora, si añadimos una instancia de NewsChannel a la lista de observadores , y cambiar el estado de la agencia de noticias , la instancia de NewsChannel será actualizada:

NewsAgency observable = new NewsAgency(); NewsChannel observer = new NewsChannel(); observable.addObserver(observer); observable.setNews("news"); assertEquals(observer.getNews(), "news");

Hay una interfaz de Observer predefinida en las bibliotecas centrales de Java, que hace que la implementación del patrón de observador sea aún más simple. Veámoslo.

3. Implementación con Observer

La interfaz java.util.Observer define el método update () , por lo que no es necesario definirlo nosotros mismos como hicimos en la sección anterior.

Veamos cómo podemos usarlo en nuestra implementación:

public class ONewsChannel implements Observer { private String news; @Override public void update(Observable o, Object news) { this.setNews((String) news); } } 

Aquí, el segundo argumento proviene de Observable como veremos a continuación.

Para definir lo observable , necesitamos extender la clase Observable de Java :

public class ONewsAgency extends Observable { private String news; public void setNews(String news) { this.news = news; setChanged(); notifyObservers(news); } }

Tenga en cuenta que no es necesario llamar directamente al método update () del observador . Simplemente llamamos a setChanged () y notifyObservers () , y la clase Observable hace el resto por nosotros.

Además, contiene una lista de observadores y expone métodos para mantener esa lista: addObserver () y deleteObserver ().

Para probar el resultado, solo necesitamos agregar el observador a esta lista y configurar las noticias:

ONewsAgency observable = new ONewsAgency(); ONewsChannel observer = new ONewsChannel(); observable.addObserver(observer); observable.setNews("news"); assertEquals(observer.getNews(), "news");

La interfaz de Observer no es perfecta y está obsoleta desde Java 9. Una de sus desventajas es que Observable no es una interfaz sino una clase, por eso las subclases no se pueden usar como observables.

Además, un desarrollador podría anular algunos de los métodos sincronizados de Observable e interrumpir su seguridad de subprocesos.

Veamos la interfaz ProperyChangeListener , que se recomienda en lugar de utilizar Observer .

4. Implementación con PropertyChangeListener

En esta implementación, un observable debe mantener una referencia a la instancia de PropertyChangeSupport . Ayuda a enviar notificaciones a los observadores cuando se cambia una propiedad de la clase.

Definamos lo observable:

public class PCLNewsAgency { private String news; private PropertyChangeSupport support; public PCLNewsAgency() { support = new PropertyChangeSupport(this); } public void addPropertyChangeListener(PropertyChangeListener pcl) { support.addPropertyChangeListener(pcl); } public void removePropertyChangeListener(PropertyChangeListener pcl) { support.removePropertyChangeListener(pcl); } public void setNews(String value) { support.firePropertyChange("news", this.news, value); this.news = value; } }

Con este soporte , podemos agregar y eliminar observadores, y notificarles cuando el estado de los observables cambia:

support.firePropertyChange("news", this.news, value);

Aquí, el primer argumento es el nombre de la propiedad observada. El segundo y tercer argumento son su valor antiguo y nuevo en consecuencia.

Los observadores deben implementar PropertyChangeListener :

public class PCLNewsChannel implements PropertyChangeListener { private String news; public void propertyChange(PropertyChangeEvent evt) { this.setNews((String) evt.getNewValue()); } }

Debido a la clase PropertyChangeSupport que está haciendo el cableado por nosotros, podemos restaurar el nuevo valor de propiedad del evento.

Probemos la implementación para asegurarnos de que también funciona:

PCLNewsAgency observable = new PCLNewsAgency(); PCLNewsChannel observer = new PCLNewsChannel(); observable.addPropertyChangeListener(observer); observable.setNews("news"); assertEquals(observer.getNews(), "news");

5. Conclusión

En este artículo, hemos examinado dos formas de implementar el patrón de diseño Observer en Java, siendo el preferido el enfoque PropertyChangeListener .

El código fuente del artículo está disponible en GitHub.