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
GO TO FULL VERSION