Autumn SALE
Obserwator

Obserwator w języku Java

Obserwator to behawioralny wzorzec projektowy pozwalający obiektom powiadamiać inne obiekty o zmianach swojego stanu.

Obserwator daje możliwość subskrypcji lub zrezygnowania z subskrypcji zdarzeń dowolnego obiektu implementującego interfejs subskrybenta.

Złożoność:

Popularność:

Przykłady użycia: Wzorzec Obserwator jest dość powszechny w kodzie Java, szczególnie w komponentach graficznego interfejsu użytkownika. Pozwala reagować na zdarzenia dotyczące innych obiektów bez konieczności sprzęgania z klasami tych obiektów.

Oto parę przykładów użycia wzorca w głównych bibliotekach Java:

Identyfikacja: Wzorzec Obserwator można poznać po obecności metod służących subskrypcji, które przechowują obiekty w strukturze listy i po wywołaniach metod aktualizacji obiektów z tej listy.

Subskrybowanie zdarzeń

W poniższym przykładzie, wzorzec Obserwator umożliwia pośrednią współpracę pomiędzy obiektami edytora tekstu. Za każdym razem, gdy obiekt Editor ulega zmianie, informuje o tym zdarzeniu subskrybentów. Metody EmailNotificationListener oraz LogOpenListener reagują na te powiadomienia przystępując do wykonywania swoich głównych zadań.

Klasy subskrybentów nie są sprzęgnięte z klasą edytora i mogą zostać wykorzystane ponownie w innych aplikacjach, jeśli zachodzi taka potrzeba. Klasa Editor zależy tylko od abstrakcyjnego interfejsu subskrybenta. Pozwala to dodawać kolejne typy subskrybentów bez konieczności zmiany kodu edytora.

publisher

publisher/EventManager.java: Bazowy publikujący

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: Konkretny publikujący, obserwowany przez inne obiekty

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: Wspólny interfejs obserwatora

package refactoring_guru.observer.example.listeners;

import java.io.File;

public interface EventListener {
    void update(String eventType, File file);
}

listeners/EmailNotificationListener.java: Wysyła emaile otrzymawszy powiadomienie

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: Zapisuje komunikat do dziennika otrzymawszy powiadomienie

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: Kod inicjalizacyjny

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: Wynik działania

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

Obserwator w innych językach

Obserwator w języku C# Obserwator w języku C++ Obserwator w języku Go Obserwator w języku PHP Obserwator w języku Python Obserwator w języku Ruby Obserwator w języku Rust Obserwator w języku Swift Obserwator w języku TypeScript