Patrón Observador Java

En el post anterior revisamos todos los detalles del patrón Observador utilizando nuestras propias interfaces. Esta vez, vamos a utilizar las clases e interfaces que Java ofrece por defecto dentro del JDK (Patrón Observador Java).

Java nos ofrece las siguientes clases dentro del paquete java.util:

  • Observable: Esta es una clase, no una interfaz. Esta clase es la que se encarga de administrar todos los observadores, es decir, registrarlos, eliminarlos y notificarlos.
  • Observer: Esta interfaz debe ser implementada por aquellos objetos que quieran recibir las diferentes notificaciones o actualizaciones enviadas por los objetos de tipo Observable.

Veamos el diagrama de clases que propone Head First Design Patterns.

Diagrama de clase que utiliza la versión de Java del patrón observador

Al hacer uso de las clases que nos brinda Java por defecto, podemos implementar dos formas de notificar a todos los observadores, una es push y la otra es pull.

Cuando queremos utilizar push, debemos llamar al método notifyObservers(Object arg). El parámetro arg sería la información que deseamos enviar.

Por otra parte, si deseamos utilizar pull, debemos llamar al método notifyObservers(). Como podemos ver, no estamos enviando ningún parámetro, lo que significa que vamos a obtener la información directamente desde el objeto que extiende de Observable.

Veamos los pasos necesarios para implementar de forma correcta este patrón utilizando las clases que nos brinda Java.

  1. Extienda de la clase Observable en aquellos objetos que necesitan enviar información cada vez que existe un cambio.
  2. Invoque al método setChanged() en los objetos que extienden de Observable, cada vez que exista un cambio que desee notificar. Este paso es muy importante, ya que este método es utilizado para verificar, si es necesario o no, notificar a los observadores. Si no se invoca, los observadores nunca van a recibir las notificaciones.
  3. Invoque a alguno de los métodos notifyObservers, por ejemplo, si desea hacer push de la información utilice notifyObservers(Object arg), si desea hacer pull utilice notifyObservers().
  4. Implemente la interfaz Observer en todos los objetos que necesitan recibir las actualizaciones con la última versión de los cambios.
  5. Implemente el método update(Observable o, Object arg) para recibir las notificaciones. En este método, si se utilizó push, la información está almacenada en el parámetro arg. Si se utilizó pull, arg es nulo.

Al aplicar los cambios para utilizar las clases que nos ofrece Java, nuestro diagrama de clases del post anterior cambia de la siguiente forma.

Diagrama de clases del ejemplo que utiliza el patrón observador

Este sería el resultado de correr el código con los nuevos cambios.

Resultado de ejecutar la aplicación de ejemplo

Comparemos por un momento el resultado de utilizar nuestras propias interfaces (izquierda), contra las clases que nos ofrece Java (derecha).

Comparación de los resultados de ejecutar las aplicaciones de ejemplo

Si utilizamos nuestra propia versión del patrón, los observadores son notificados en el orden en que estén almacenados en la lista que definimos. Mientras que si utilizamos la versión que nos ofrece Java, el orden en que los observadores son notificados es aleatorio.

Debido a esto, nunca debemos depender del orden en que los observadores son notificados, ya que estaríamos acoplando nuestra lógica a un proceso que siempre usa un orden diferente y como hemos visto antes, iría en contra de los principios de diseño.

Ahora bien, lastimosamente, no todo es perfecto cuando usamos las clases que nos ofrece Java para este patrón, ya que Observable es una clase, es decir si nuestro objeto ya extiende de otra clase no podríamos extenderla para agregar esta nueva funcionalidad. Aquí estamos violando el principio de diseño que nos dice que deberíamos de programar en las interfaces y no en las implementaciones.

Además, no podemos crear nuestra propia implementación, por ejemplo para poder cambiar el tipo en tiempo de ejecución, como hemos visto en otros casos o poder soportar múltiples hilos.

Entonces, ¿debemos utilizar las clases que nos ofrece Java? La respuesta es depende de lo que necesitemos en nuestra aplicación.

Si deseamos una implementación rápida y poco compleja del patrón, entonces podemos utilizar las clases de Java. Si, por el contrario, necesitamos un diseño más robusto y tener un mayor control sobre como interactúan los observadores con los temas (Subject) deberíamos de optar por utilizar nuestra propia versión del patrón.

¿En qué otras clases es utilizado el patrón Observador dentro de Java?

Java utiliza este patrón en los JavaBeans y en algunos elementos dentro del paquete Swing, como por ejemplo JButton. En este último, es utilizado para registrar eventos asignados al botón. Por ejemplo, cuando se presiona el botón o cuando se posiciona el mouse encima de él.

El código de ejemplo del patrón observador JAVA lo pueden descargar aquí.

En el próximo post vamos a analizar el patrón llamado Método Plantilla.

Recordá suscribirte aquí para recibir las últimas actualizaciones todas las semanas.

Referencias:
Libro Head First Design Patterns. Versión digital.

Leave a Reply

Your email address will not be published. Required fields are marked *