JavaRush /Java Blog /Random-IT /Modello di osservatore

Modello di osservatore

Pubblicato nel gruppo Random-IT
Come scrive Gang of Four (riferendosi al libro “Object-Oriented Design Patterns” di 4 sviluppatori di prim'ordine), lo scopo di questo pattern è quello di definire una dipendenza uno-a-molti tra gli oggetti in modo tale che quando il lo stato di un oggetto cambia, tutti quelli da esso dipendenti vengono avvisati e vengono aggiornati automaticamente. Questo modello è anche chiamato: Dipendenti (subordinati) o Publish-Subscribe (editore - abbonato). Ma proviamo a capirlo usando l'esempio della Chiesa cattolica :) Ha seguaci che credono negli insegnamenti di questa chiesa. Quando compaiono nuovi dogmi (credi obbligatori) e altro ancora, queste persone dovrebbero conoscerli. Ma come potrebbe essere descritto questo in un linguaggio di programmazione utilizzando questo modello? 1. Abbiamo la “voce della chiesa” (la chiesa stessa o il Papa quando trasmette ex cathedra), cioè una certa emittente o soggetto che annuncia notizie nella chiesa. 2. Ci sono parrocchiani di questa chiesa, cioè alcuni osservatori che vogliono essere a conoscenza di eventi importanti. Di conseguenza, oggi potrebbero esserci 1,3 miliardi di parrocchiani e domani potrebbero essercene più o meno. E devi solo avvisare coloro che sono in questa chiesa (non c'è bisogno di disturbare nuovamente gli atei :). Quindi, tutto ciò potrebbe essere espresso così: c'è una chiesa che racconterà qualcosa al suo gregge, nella quale puoi iscriverti o, al contrario, lasciarla:
public interface Church {
    void registerParishioner(Parishioner parishioner);
    void removeParishioner(Parishioner parishioner);
    void notifyParishioners();
}
Esiste una Chiesa cattolica specifica con l'implementazione di questi metodi, nonché notizie e un elenco di persone a cui queste notizie dovrebbero essere trasmesse:
public class CatholicChurch implements Church {
    private List<parishioner> parishioners;
    private String newsChurch;

    public CatholicChurch() {
        parishioners = new ArrayList<>();
    }

    public void setNewsChurch(String news) {
        this.newsChurch = news;
        notifyParishioners();
    }

    @Override
    public void registerParishioner(Parishioner parishioner) {
        parishioners.add(parishioner);
    }

    @Override
    public void removeParishioner(Parishioner parishioner) {
        parishioners.remove(parishioner);
    }

    @Override
    public void notifyParishioners() {
        for (Parishioner parishioner : parishioners)
            parishioner.update(newsChurch);
    }
}
C'è un parrocchiano che può entrare o uscire dalla chiesa (per semplificare il codice, consentiremo l'ingresso solo a lui :)
public class Parishioner {

    private String name;

    public Parishioner(String name, Church church) {
        this.name = name;
        church.registerParishioner(this);
    }

    void update(String newsChurch) {
        System.out.println(name + "узнал новость: " + newsChurch);
    }
}
Di conseguenza, ecco come funzionerà:
public class Main {
    public static void main(String[] args) {
        var catholicChurch = new CatholicChurch();

        new Parishioner("Мартин Лютер", catholicChurch);
        new Parishioner("Жан Кальвин", catholicChurch);

        catholicChurch.setNewsChurch("Инквизиция была ошибкой... месса Mea Culpa 12 марта 2000 года");
    }
}
e il risultato del programma:
Мартин Лютер узнал новость: Инквизиция была ошибкой... месса Mea Culpa 12 марта 2000 года
Жан Кальвин узнал новость: Инквизиция была ошибкой... месса Mea Culpa 12 марта 2000 года
Quelli. Non appena appariranno notizie nella chiesa, tutti coloro che fanno parte dell'elenco dei membri registrati di questa chiesa ne verranno informati. Quali sono gli svantaggi di questa implementazione: 1. In primo luogo, l'interfaccia dove è possibile registrarsi e ricevere notizie potrebbe riguardare non solo questa chiesa (potrebbe essere necessario). Pertanto, sarebbe possibile spostarlo immediatamente in un'interfaccia Observable separata. 2. Lo stesso potrebbe essere fatto con i parrocchiani della chiesa, vale a dire spostare il metodo di aggiornamento in un'interfaccia separata e implementarlo per il parrocchiano desiderato. Quindi questo metodo potrà essere utilizzato in generale non dai parrocchiani della Chiesa cattolica, ma, ad esempio, da coloro che vivono nell'esistenza degli elfi (intendendo il movimento “La strada verso l'unicorno”). Quelli. creare un'interfaccia Observer con un metodo di aggiornamento. Cosa accadrà alla fine:
interface Observable {
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObservers();
}
public class CatholicChurch implements Observable {
    private List<observer> parishioners;
    private String newsChurch;

    public CatholicChurch() {
        parishioners = new ArrayList<>();
    }

    public void setNewsChurch(String news) {
        this.newsChurch = news;
        notifyObservers();
    }

    @Override
    public void registerObserver(Observer o) {
        parishioners.add(o);
    }

    @Override
    public void removeObserver(Observer o) {
        parishioners.remove(o);
    }

    @Override
    public void notifyObservers() {
        for (Observer o : parishioners)
            o.update(newsChurch);
    }
}
interface Observer {
    void update (String news);
}
public class Parishioner implements Observer {
    private String name;

    public Parishioner(String name, Observable o) {
        this.name = name;
        o.registerObserver(this);
    }

    @Override
    public void update(String news) {
        System.out.println(name + " узнал новость: " + news);
    }
}
Quindi: abbiamo "indebolito il legame" tra chiesa e parrocchiani, il che naturalmente è positivo solo nella programmazione :) Il soggetto (Chiesa cattolica) ha solo un elenco di ascoltatori (parrocchiani) e quando riceve notizie (cambiamenti), trasmette queste notizie ai suoi ascoltatori. Ora puoi creare qualsiasi altra entità (ad esempio, una chiesa protestante) e trasmettere le notizie ai “tuoi” ascoltatori. È inoltre necessario tenere conto del fatto che i dati della classe 2 (più precisamente, la classe Observable e l'interfaccia Observer) erano disponibili nel pacchetto java java.util, ma ora sono deprecati con Java 9 (https://docs.oracle. com/en/java/javase/15/docs/api/java.base/java/util/Observable.html): deprecato. Questa classe e l'interfaccia Observer sono state deprecate. Il modello di eventi supportato da Observer e Observable è piuttosto limitato, l'ordine delle notifiche inviate da Observable non è specificato e i cambiamenti di stato non sono in corrispondenza uno a uno con le notifiche. Per un modello di eventi più ricco, considera l'utilizzo del pacchetto java.beans. Per una messaggistica affidabile e ordinata tra i thread, considera l'utilizzo di una delle strutture dati simultanee nel pacchetto java.util.concurrent. Per la programmazione in stile flussi reattivi, consulta l'API Flow. Pertanto non è necessario utilizzarli. E puoi usarne altri invece, ma l'essenza del modello non cambierà. Ad esempio, proviamo a utilizzare PropertyChangeListener (per non scrivere classi non necessarie che sono già state scritte) dal pacchetto java.beans. Vediamo come sarà: classe soggetto:
public class CatholicChurch {
    private String news;
    // используя support мы можем добавлять or удалять наших прихожан (слушателей)
    private PropertyChangeSupport support;

    public CatholicChurch() {
        support = new PropertyChangeSupport(this);
    }
    public void addPropertyChangeListener(PropertyChangeListener pcl) {
        support.addPropertyChangeListener(pcl);
    }

    public void removePropertyChangeListener(PropertyChangeListener pcl) {
        support.removePropertyChangeListener(pcl);
    }

    public void setNews(String value) {
        support.firePropertyChange("news", this.news, value);
        this.news = value;
    }
}
e la classe ascoltatore:
public class Parishioner implements PropertyChangeListener {
    private String name;

    public Parishioner(String name) {
        this.name = name;
    }

    public void propertyChange(PropertyChangeEvent evt) {
        this.setNews((String) evt.getNewValue());
    }

    public void setNews(String news) {
        System.out.println(name + " узнал новость: " + news);
    }
}
Se eseguiamo il seguente codice:
public static void main(String[] args) {
    CatholicChurch observable = new CatholicChurch();

    observable.addPropertyChangeListener(new Parishioner("Мартин Лютер"));
    observable.addPropertyChangeListener(new Parishioner("Жан Кальвин"));

    observable.setNews("Дева Мария имеет непорочное зачатие... булла Ineffabilis Deus... 8 декабря 1854 года Папа Пий IX");
    observable.setNews("Папа непогрешим... не всегда конечно, а только когда транслирует учение церкви ex cathedra... Первый Ватиканский собор 1869 год");
}
Otteniamo il seguente risultato:
Martin Lutero apprese la notizia: La Vergine Maria è dell'Immacolata Concezione... bolla Ineffabilis Deus... 8 dicembre 1854 Papa Pio IX Giovanni Calvino apprese la notizia: La Vergine Maria è dell'Immacolata Concezione... Bolla Ineffabilis Deus ... 8 dicembre 1854 Papa Pio IX Martin Lutero apprende la notizia: Il Papa è infallibile... non sempre ovviamente, ma solo quando trasmette gli insegnamenti della chiesa ex cathedra... Concilio Vaticano I 1869 Giovanni Calvino apprende la notizia: Il Papa è infallibile... non sempre ovviamente, ma solo quando trasmette l'insegnamento della Chiesa ex cathedra... Concilio Vaticano I 1869
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION