Patrón Observer – Ejemplo de aplicación del patrón en Java

El patrón de diseño Observer se caracteriza principalmente por la reducción en el acoplamiento entre distintos componentes de una aplicación. Si nos encontramos en una situación en la que uno de estos componentes se acopla con el resto de ellos, como en el ejemplo de código que vamos a ver, siguiendo el patrón de diseño Observer daremos a este componente el rol de sujeto, el resto serán observadores.

La ventaja que proporciona el uso del patrón observador es que el código de la clase que hace de sujeto, que es la que tiene la información de cuando y por qué se deben hacer ciertas «tareas», quedará liberada de llamar directamente a los componentes que realizan estas «tareas». Dado que, llamarlos directamente implica conocer el tipo concreto de estos componentes y su interfaz concreta, se produce el acoplamiento con estos.

En lugar de esto, la clase sujeto notificará a los componentes cuando lo estime conveniente porque se da el motivo para ello. El patrón Obervador facilita que la notificación se produzca a través de una interfaz bien definida y común a todos estos componentes que se va a notificar. Esta interfaz común viene dada mediante la interfaz Observer que cuenta con un método que se suele denominar update().

Es tarea del sujeto mantener una lista de objetos interesados en ser notificados, con una serie métodos (register y unregister) para que los componentes observadores se registeren y se den de baja en esta lista. El sujeto pues, ya no necesita conocer el tipo concreto de los componentes, el tipo de las referencias con las que trabaja para notificar a los componentes es del tipo abstracto Observer. Tampoco depende de cuantos componentes estén registrados y si reciben la notificación ni de como la procesan.

En conclusión, todo esto permite que al programa al que aplicamos el patrón observador, podamos añadir nuevos componentes adicionales que amplien la funcionalidad con nuevas tareas en futuras modificaciones del código sin que sea necesario hacer cambios en el código de la clase notificadora o sujeto. Bastará simplemente con instanciar un objeto de este nuevo componente y registrarlo para que observe al sujeto.

En el ejemplo, tenemos una clase para autenticar usuarios, su responsabilidad es comprobar unas credenciales (usuario y contraseña) y validarlas, concluyendo si son correctas o no. Por otro lado, existen tareas adicionales, dependientes de si la validación ha tenido exito o no, son variadas y abarcan desde la carga de archivos del perfil, el registro de auditoria, la monitorización de ataques, etc.

Si todo esto lo hacemos en el código de la clase de autenticación estamos rompiendo el principio de responsabilidad única, y lo peor, estamos creando una dependencia en esta clase por cada uno de estos componentes que maneja directamente. Ampliaciones en este sistema para que trabaje con un nuevo compomente implica cambios en el código de la clase de autenticación. Los cambios en los componentes existentes tambien pueden afectar al código de la clase de autenticación.

Daremos a la clase que autentica el rol de sujeto, porque es la que tiene conocimiento de CUANDO suceden la acción que da lugar a que sea necesario escribir una entrada en el registro de eventos, cargar perfiles, activar la monitización de ataques, etc. y el rol de observadores al resto de componentes porque son los que tienen el conocimiento de COMO registrar, cargar, monitorizar, etc. Estos se registran en la lista de observadores del sujeto y este les noficará cuando deben realizar su labor, además de la información adicional necesaria.

Aqui os dejo el video tutorial.

El asunto de la comunicación adicional que el observador necesita para realizar su trabajo no es una cuestión menor y existen varias posibilidades. Estas variantes del patrón Obsever en función de como se facilita la información adicional a los observadores se denominan Push y Pull. En el próximo post analizaremos cada una de ellas para ver sus ventajas e inconvenientes.