JavaRush /Blog Java /Random-PL /Szablon obserwatora

Szablon obserwatora

Opublikowano w grupie Random-PL
Jak pisze Gang of Four (odwołując się do książki „Object-Oriented Design Patterns” autorstwa 4 czołowych programistów), celem tego wzorca jest zdefiniowanie zależności jeden do wielu pomiędzy obiektami w taki sposób, że gdy zmienia się stan jednego obiektu, wszystkie osoby od niego zależne są o tym powiadamiane i automatycznie aktualizowane. Wzorzec ten nazywany jest także: Zależni (podwładni) lub Publikuj-Subskrybuj (wydawca - subskrybent). Ale spróbujmy to rozgryźć na przykładzie Kościoła katolickiego :) Ma on wyznawców wierzących w naukę tego kościoła. Kiedy pojawią się jakieś nowe dogmaty (obowiązkowe wyznania wiary) i inne, ci ludzie powinni o nich wiedzieć. Ale jak można to opisać w języku programowania przy użyciu tego wzorca? 1. Mamy „głos Kościoła” (samego Kościoła lub Papieża w przypadku nadawania ex cathedra), czyli pewnego nadawcę lub podmiot, który ogłasza wiadomości w kościele. 2. Są parafianie tego kościoła, czyli niektórzy obserwatorzy, którzy chcą być świadomi ważnych wydarzeń. Zatem dzisiaj może być 1,3 miliarda parafian, a jutro może być mniej więcej. A wystarczy powiadomić tych, którzy są w tym kościele (nie ma potrzeby znowu zawracać głowy ateistom :). Można to więc wszystko wyrazić następująco: Jest taki kościół, który powie o czymś swojej owczarni, w której można się zarejestrować lub wręcz przeciwnie, opuścić:
public interface Church {
    void registerParishioner(Parishioner parishioner);
    void removeParishioner(Parishioner parishioner);
    void notifyParishioners();
}
Istnieje konkretny Kościół katolicki z wdrażaniem tych metod, a także wiadomości i lista osób, którym należy tę wiadomość przekazać:
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);
    }
}
Do kościoła może wejść lub wyjść parafianin (dla uproszczenia kodu pozwolimy tylko mu wejść :)
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);
    }
}
W związku z tym będzie to działać w następujący sposób:
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 года");
    }
}
i wynik programu:
Мартин Лютер узнал новость: Инквизиция была ошибкой... месса Mea Culpa 12 марта 2000 года
Жан Кальвин узнал новость: Инквизиция была ошибкой... месса Mea Culpa 12 марта 2000 года
Te. gdy tylko w kościele pojawi się wiadomość, wszyscy, którzy znajdą się w gronie zarejestrowanych członków tego kościoła, zostaną o tym powiadomieni. Jakie są wady tej implementacji: 1. Po pierwsze, interfejs, w którym można się zarejestrować i otrzymywać wiadomości, może dotyczyć nie tylko tego kościoła (może to być wymagane). Dlatego możliwe byłoby natychmiastowe przeniesienie tego do osobnego interfejsu Observable. 2. To samo można zrobić z parafianami kościoła, a mianowicie przenieść metodę aktualizacji do osobnego interfejsu i wdrożyć ją dla wybranego parafianina. Wtedy tę metodę będą mogli zastosować w ogóle nie parafianie Kościoła katolickiego, ale np. Ci, którzy żyją w egzystencji elfów (czyli ruch „Droga do Jednorożca”). Te. utwórz interfejs Observer z metodą aktualizacji. Co się stanie na końcu:
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);
    }
}
A zatem: „Osłabiliśmy więź” pomiędzy kościołem a parafianami, co jest oczywiście dobre tylko w programowaniu :) Temat (Kościół katolicki) ma jedynie listę słuchaczy (parafian) i po otrzymaniu wiadomości (zmiany) nadaje tę wiadomość swoim słuchaczom. Teraz możesz stworzyć dowolny inny podmiot (na przykład kościół protestancki) i transmitować wiadomości „swoim” słuchaczom. Trzeba też wziąć pod uwagę, że dane klasy 2 (dokładniej klasa Observable i interfejs Observer) były dostępne w pakiecie Java java.util, ale teraz są one Deprecation z Java 9 (https://docs.oracle. com/en/java/javase/15/docs/api/java.base/java/util/Observable.html): Przestarzałe. Ta klasa i interfejs Observer są przestarzałe. Model zdarzeń obsługiwany przez Observer i Observable jest dość ograniczony, kolejność powiadomień dostarczanych przez Observable jest nieokreślona, ​​a zmiany stanu nie pokrywają się jeden do jednego z powiadomieniami. Aby uzyskać bogatszy model zdarzeń, rozważ użycie pakietu java.beans. Aby zapewnić niezawodne i uporządkowane przesyłanie komunikatów między wątkami, rozważ użycie jednej ze współbieżnych struktur danych w pakiecie java.util.concurrent. Aby zapoznać się z programowaniem w stylu strumieni reaktywnych, zobacz API Flow. Dlatego nie ma potrzeby ich używać. I zamiast tego możesz użyć innych, ale istota wzoru się nie zmieni. Na przykład spróbujmy użyć PropertyChangeListener (aby nie pisać dodatkowych klas, które są już napisane) z pakietu java.beans. Zobaczymy jak to będzie: klasa przedmiotowa:
public class CatholicChurch {
    private String news;
    // используя support мы можем добавлять Lub удалять наших прихожан (слушателей)
    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;
    }
}
i klasa słuchacza:
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);
    }
}
Jeśli wykonamy następujący kod:
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 год");
}
Otrzymujemy następujący wynik:
Marcin Luter dowiedział się tej wiadomości: Dziewica Maryja jest Niepokalanie Poczęta... bulla Ineffabilis Deus... 8 grudnia 1854 Papież Pius IX Jan Kalwin dowiedział się wiadomości: Dziewica Maryja jest Niepokalanie Poczęta... Bulla Ineffabilis Deus ... 8 grudnia 1854 Papież Pius IX Marcin Luter dowiedział się o wiadomości: Papież jest nieomylny... nie zawsze oczywiście, ale tylko wtedy, gdy ogłasza nauczanie Kościoła ex cathedra... I Sobór Watykański 1869 Jan Kalwin dowiedział się wiadomość: Papież jest nieomylny... nie zawsze oczywiście, ale tylko wtedy, gdy ogłasza nauczanie Kościoła ex cathedra... Sobór Watykański I 1869
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION