![Observer](/images/patterns/cards/observer-mini.png?id=fd2081ab1cff29c60b499bcf6a62786a)
Observer en Java
Observer es un patrón de diseño de comportamiento que permite a un objeto notificar a otros objetos sobre cambios en su estado.
El patrón Observer proporciona una forma de suscribirse y cancelar la subscripción a estos eventos para cualquier objeto que implementa una interfaz suscriptora.
Complejidad:
Popularidad:
Ejemplos de uso: El patrón Observer es bastante habitual en el código Java, sobre todo en los componentes GUI. Proporciona una forma de reaccionar a los eventos que suceden en otros objetos, sin acoplarse a sus clases.
Aquí tienes algunos ejemplos del patrón en las principales bibliotecas Java:
-
java.util.Observer
/java.util.Observable
(rara vez utilizado en el mundo real) - Todas las implementaciones de
java.util.EventListener
(prácticamente por todos los componentes Swing) -
javax.servlet.http.HttpSessionBindingListener
-
javax.servlet.http.HttpSessionAttributeListener
-
javax.faces.event.PhaseListener
Identificación: El patrón puede reconocerse por los métodos de subscripción, que almacenan objetos en una lista, y por las llamadas al método de actualización emitidas a todos los objetos de esa lista.
Suscripción a eventos
En este ejemplo, el patrón Observer establece una colaboración indirecta entre objetos de un editor de texto. Cada vez que el objeto Editor
cambia, lo notifica a sus suscriptores. EmailNotificationListener
y LogOpenListener
reaccionan a esas notificaciones ejecutando sus principales comportamientos.
Las clases suscriptoras no están acopladas a la clase editora y pueden reutilizarse en otras aplicaciones si fuera necesario. La clase Editor
depende únicamente de la interfaz suscriptora abstracta. Esto permite añadir nuevos tipos de suscriptor sin cambiar el código del editor.
publisher
publisher/EventManager.java: Notificador básico
package refactoring_guru.observer.example.publisher;
import refactoring_guru.observer.example.listeners.EventListener;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class EventManager {
Map<String, List<EventListener>> listeners = new HashMap<>();
public EventManager(String... operations) {
for (String operation : operations) {
this.listeners.put(operation, new ArrayList<>());
}
}
public void subscribe(String eventType, EventListener listener) {
List<EventListener> users = listeners.get(eventType);
users.add(listener);
}
public void unsubscribe(String eventType, EventListener listener) {
List<EventListener> users = listeners.get(eventType);
users.remove(listener);
}
public void notify(String eventType, File file) {
List<EventListener> users = listeners.get(eventType);
for (EventListener listener : users) {
listener.update(eventType, file);
}
}
}
editor
editor/Editor.java: Notificador concreto, rastreado por otros objetos
package refactoring_guru.observer.example.editor;
import refactoring_guru.observer.example.publisher.EventManager;
import java.io.File;
public class Editor {
public EventManager events;
private File file;
public Editor() {
this.events = new EventManager("open", "save");
}
public void openFile(String filePath) {
this.file = new File(filePath);
events.notify("open", file);
}
public void saveFile() throws Exception {
if (this.file != null) {
events.notify("save", file);
} else {
throw new Exception("Please open a file first.");
}
}
}
listeners
listeners/EventListener.java: Interfaz observadora común
package refactoring_guru.observer.example.listeners;
import java.io.File;
public interface EventListener {
void update(String eventType, File file);
}
listeners/EmailNotificationListener.java: Envía correos electrónicos al recibir la notificación
package refactoring_guru.observer.example.listeners;
import java.io.File;
public class EmailNotificationListener implements EventListener {
private String email;
public EmailNotificationListener(String email) {
this.email = email;
}
@Override
public void update(String eventType, File file) {
System.out.println("Email to " + email + ": Someone has performed " + eventType + " operation with the following file: " + file.getName());
}
}
listeners/LogOpenListener.java: Escribe un mensaje a un registro al recibir una notificación
package refactoring_guru.observer.example.listeners;
import java.io.File;
public class LogOpenListener implements EventListener {
private File log;
public LogOpenListener(String fileName) {
this.log = new File(fileName);
}
@Override
public void update(String eventType, File file) {
System.out.println("Save to log " + log + ": Someone has performed " + eventType + " operation with the following file: " + file.getName());
}
}
Demo.java: Código de inicialización
package refactoring_guru.observer.example;
import refactoring_guru.observer.example.editor.Editor;
import refactoring_guru.observer.example.listeners.EmailNotificationListener;
import refactoring_guru.observer.example.listeners.LogOpenListener;
public class Demo {
public static void main(String[] args) {
Editor editor = new Editor();
editor.events.subscribe("open", new LogOpenListener("/path/to/log/file.txt"));
editor.events.subscribe("save", new EmailNotificationListener("admin@example.com"));
try {
editor.openFile("test.txt");
editor.saveFile();
} catch (Exception e) {
e.printStackTrace();
}
}
}
OutputDemo.txt: Resultado de la ejecución
Save to log \path\to\log\file.txt: Someone has performed open operation with the following file: test.txt
Email to admin@example.com: Someone has performed save operation with the following file: test.txt