Наблюдатель на Java
Наблюдатель — это поведенческий паттерн, который позволяет объектам оповещать другие объекты об изменениях своего состояния.
При этом наблюдатели могут свободно подписываться и отписываться от этих оповещений.
Сложность:
Популярность:
Применимость: Наблюдатель можно часто встретить в Java коде, особенно там, где применяется событийная модель отношений между компонентами. Наблюдатель позволяет отдельным компонентам реагировать на события, происходящие в других компонентах.
Примеры Наблюдателя в стандартных библиотеках Java:
java.util.Observer/java.util.Observable(редко используется в реальной жизни)- Все реализации
java.util.EventListener(практически во всём Swing-е) javax.servlet.http.HttpSessionBindingListenerjavax.servlet.http.HttpSessionAttributeListenerjavax.faces.event.PhaseListener
Признаки применения паттерна: Наблюдатель можно определить по механизму подписки и методам оповещения, которые вызывают компоненты программы.
Подписка и оповещения
В этом примере Наблюдатель используется для передачи событий между объектами текстового редактора. Всякий раз когда объект редактора меняет своё состояние, он оповещает своих наблюдателей. Объекты EmailNotificationListener и LogOpenListener следят за этими уведомлениями и выполняют полезную работу в ответ.
Классы подписчиков не связаны с классом редактора и могут быть повторно использованы в других приложениях если потребуется. Класс Editor зависит только от общего интерфейса подписчиков. Это позволяет добавлять новые типы подписчиков не меняя существующего кода редактора.
publisher
publisher/EventManager.java: Базовый издатель
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: Конкретный издатель, изменение которого хотят отслеживать наблюдатели
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: Интерфейс подписчиков
package refactoring_guru.observer.example.listeners; import java.io.File; public interface EventListener { void update(String eventType, File file); } listeners/EmailNotificationListener.java: Слушатель, рассылающий email-уведомления
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: Слушатель, записывающий лог операций
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: Клиентский код
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: Результат выполнения
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