Análisis Práctico del Patrón Observador
Generalmente, en el análisis y diseño orientado a objetos existen tres mecanismos para el manejo de eventos: además de la llamada ordinaria de funciones, se utilizan frecuentemente las funciones de retorno (callbacks), y J2EE proporciona un mecanismo de manejo de eventos basado en la escucha (listeners). Consulta la documentación, analiza el mecanismo de Action y ActionListener, y completa un ejemplo de análisis.
Primero, comprendamos el patrón Observador.
Patrón Observador (Observer Pattern)
Observadores en el mundo real
El patrón Observador también se conoce como patrón Publicar-Suscribir. Por ejemplo, en las suscripciones RSS (el precursor de las notificaciones push de WeChat), cuando el autor de un blog (el sujeto observado) actualiza una entrada, los lectores (observadores) de los suscriptores reciben automáticamente la actualización. Este esquema de publicación-suscripción lo llamamos patrón Observador.
El patrón Observador
La descripción del patrón Observador por el Grupo de los Cuatro (Gang Of Four):
Intención
Definir una relación de dependencia uno-a-muchos entre objetos, de modo que cuando un objeto cambie de estado, todos los objetos que dependen de él sean notificados y actualizados automáticamente.
Alias
Dependientes (Dependents), Publicar-Suscribir (Publish-Subscribe)
Motivación
Dividir un sistema en una serie de clases que cooperan tiene un efecto secundario común: la necesidad de mantener la consistencia entre los objetos relacionados. No queremos que las clases estén fuertemente acopladas para mantener la consistencia, ya que esto reduce su reutilización.
…
Participantes
Sujeto (Subject)
El sujeto conoce a sus observadores. Puede haber cualquier número de observadores observando el mismo sujeto.
Proporciona una interfaz para registrar y eliminar objetos observadores.
Observador (Observer)
Define una interfaz de actualización para aquellos objetos que necesitan ser notificados cuando el sujeto cambia.
SujetoConcreto (ConcreteSubject)
Almacena el estado relevante en cada objeto ObservadorConcreto.
Cuando su estado cambia, notifica a todos sus observadores.
ObservadorConcreto (ConcreteObserver)
Mantiene una referencia a un objeto SujetoConcreto.
Almacena un estado que debe ser consistente con el estado del sujeto.
Implementa la interfaz de actualización del Observador para mantener su propio estado consistente con el estado del sujeto.
Se puede observar que el patrón Observador se aplica frecuentemente en:
- Dos objetos tienen una relación de dependencia.
- Cuando un objeto cambia, es necesario cambiar otros objetos simultáneamente.
En comparación con las llamadas ordinarias de funciones y las funciones de retorno (callbacks), el uso del patrón Observador permite mantener la consistencia entre objetos a bajo costo, reduciendo significativamente el grado de acoplamiento entre objetos, y al mismo tiempo logra un efecto de difusión (broadcast), algo que los dos primeros no pueden hacer. El uso del patrón Observador mejora enormemente la reutilización del código.
Y Action y ActionListener en J2EE son un ejemplo vivo del patrón Observador.
Action y ActionListener en J2EE
Action y ActionListener pueden aplicarse muy bien en el paradigma MVC para reducir el acoplamiento entre el modelo (model) y la vista (view).
Encontré un buen ejemplo, cuyo código se muestra a continuación:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
public class Frame extends JFrame {
ActionListener observer1 = new Observer("First Observer");
ActionListener observer2 = new Observer("Second Observer");
ActionListener observer3 = new Observer("Third Observer");
public Frame(){
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
init();
}
private void init(){
addChangingTextField();
pack();
setVisible(true);
}
private void addChangingTextField(){
//Clase observable TextField. Cada vez que
//ocurre una acción o evento, los observadores serán actualizados
//Casi todas las clases Swing tienen este tipo de comportamiento
JTextField dataTextField = new JTextField();
// Registro de observadores en la clase observable.
dataTextField.addActionListener(observer1);
dataTextField.addActionListener(observer2);
dataTextField.addActionListener(observer3);
add(dataTextField);
}
public static void main(String [] args){
Frame frame = new Frame();
}
}
class Observer implements ActionListener{
private String name;
private String message;
public Observer (String s){
name = s;
}
public void actionPerformed(ActionEvent e) {
JTextField textField = (JTextField) e.getSource();
message = textField.getText();
JOptionPane.showMessageDialog(null,
name + " ha sido notificado sobre el cambio, ¡Gracias!! "
+ " El mensaje fue: " + message);
}
}
Análisis
Las dos clases asumen los roles de Action/ActionListener respectivamente. frame.dataTextField en el método addChangingTextField vincula tres escuchas (observer1, observer2, observer3). Después de que se edita el campo de texto en el frame (cambio de estado), se dispara el método actionPerformed en Observer, mostrando el texto correspondiente.
En este ejemplo, frame.dataTextField actúa como SujetoConcreto (ConcreteSubject), y observer1, observer2, observer3 actúan como ObservadoresConcretos (ConcreteObservers). Las interfaces que implementan, que definen los métodos Action/ActionListener, son los correspondientes Sujeto (Subject) y Observador (Observer).
Después de vincular los observadores, el sujeto no se preocupa por el comportamiento específico de los observadores, sino que simplemente los notifica cuando su propio estado cambia, dejando que los observadores decidan qué acción tomar. Por lo tanto, este método es muy bueno, mucho mejor que activar acciones mediante llamadas explícitas de funciones o ejecución de funciones de retorno, vale la pena aprenderlo.