Autumn SALE
Спостерігач

Спостерігач на Java

Спостерігач — це поведінковий патерн, який дозволяє об’єктам повідомляти інші об’єкти про зміни свого стану.

При цьому спостерігачі можуть вільно підписуватися і відписуватись від цих повідомлень.

Складність:

Популярність:

Застосування: Спостерігач часто зустрічається в коді Java, особливо там, де до відносин між компонентами застосовується модель подій. Спостерігач дозволяє окремим компонентам реагувати на події, які відбуваються в інших компонентах.

Приклади Спостерігача в стандартних бібліотеках Java:

Ознаки застосування патерна: Спостерігач визначається за механізмом підписки та методами повідомлення, які викликають компоненти програми.

Підписка та повідомлення

У цьому прикладі Спостерігач використовується для передачі подій між об’єктами текстового редактора. Кожного разу, коли об’єкт редактора змінює свій стан, він повідомляє своїх спостерігачів. Об’єкти 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

Спостерігач іншими мовами програмування

Спостерігач на C# Спостерігач на C++ Спостерігач на Go Спостерігач на PHP Спостерігач на Python Спостерігач на Ruby Спостерігач на Rust Спостерігач на Swift Спостерігач на TypeScript