JavaRush /Java Blog /Random EN /Observer template

Observer template

Published in the Random EN group
As the Gang of Four writes (referring to the book “Object-Oriented Design Patterns” by 4 top-notch developers), the purpose of this pattern is to define a one-to-many dependency between objects in such a way that when the state of one object changes, all those dependent on it are notified about this and are automatically updated. This pattern is also called: Dependents (subordinates) or Publish-Subscribe (publisher - subscriber). But let's try to figure it out using the example of the Catholic Church :) It has followers who believe in the teachings of this church. When any new dogmas (obligatory creeds) and more appear, these people should know about them. But how could this be described in a programming language using this pattern? 1. We have the “voice of the church” (the church itself or the Pope when broadcasting ex cathedra), that is, a certain broadcaster or subject who announces news in the church. 2. There are parishioners of this church, that is, some observers who want to be aware of important events. Accordingly, today there may be 1.3 billion parishioners, and tomorrow there may be more or less. And you only need to notify those who are in this church (no need to bother atheists again :). Thus, all this could be expressed as follows: There is a church that will tell its flock about something, in which you can register or, on the contrary, leave it:
public interface Church {
    void registerParishioner(Parishioner parishioner);
    void removeParishioner(Parishioner parishioner);
    void notifyParishioners();
}
There is a specific Catholic Church with the implementation of these methods, as well as news and a list of people to whom this news should be broadcast:
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);
    }
}
There is a parishioner who can enter or leave the church (to simplify the code, we will only allow him to enter :)
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);
    }
}
Accordingly, this is how it will work:
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 года");
    }
}
and the result of the program:
Мартин Лютер узнал новость: Инквизиция была ошибкой... месса Mea Culpa 12 марта 2000 года
Жан Кальвин узнал новость: Инквизиция была ошибкой... месса Mea Culpa 12 марта 2000 года
Those. As soon as news appears in the church, everyone who is in the array of registered members of this church will be notified about it. What are the disadvantages of this implementation: 1. Firstly, the interface where you can register and receive news may concern not only this church (this may be required). Therefore, it would be possible to immediately move this into a separate Observable interface. 2. The same could be done with church parishioners, namely, move the update method into a separate interface and implement it for the desired parishioner. Then this method will be able to be used in general not by parishioners of the Catholic Church, but, for example, by those who live in the existence of elves (meaning the “Road to the Unicorn” movement). Those. create an Observer interface with an update method. What will happen in the end:
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);
    }
}
Thus: We have “weakened the connection” between the church and parishioners, which is naturally good only in programming :) The subject (Catholic church) has only a list of listeners (parishioners) and when receiving news (changes), broadcasts this news to its listeners. You can now create any other entity (for example, a Protestant church) and broadcast the news to “your” listeners. You also need to take into account that class 2 data (more precisely, the Observable class and the Observer interface) were available in the java.util java package, but now they are Deprecation with java 9 (https://docs.oracle.com/en/java/javase/15/ docs/api/java.base/java/util/Observable.html): Deprecated. This class and the Observer interface have been deprecated. The event model supported by Observer and Observable is quite limited, the order of notifications delivered by Observable is unspecified, and state changes are not in one-for-one correspondence with notifications. For a richer event model, consider using the java.beans package. For reliable and ordered messaging among threads, consider using one of the concurrent data structures in the java.util.concurrent package. For reactive streams style programming, see the Flow API. Therefore there is no need to use them. And you can use others instead, but the essence of the pattern will not change. For example, let's try to use PropertyChangeListener (so as not to write unnecessary classes that have already been written) from the java.beans package. Let's see how it will be: subject class:
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;
    }
}
and the listener class:
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);
    }
}
If we execute the following code:
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 год");
}
We get the following result:
Martin Luther learned the news: The Virgin Mary is of the Immaculate Conception... bull Ineffabilis Deus... December 8, 1854 Pope Pius IX John Calvin learned the news: The Virgin Mary is of the Immaculate Conception... Bull Ineffabilis Deus... December 8, 1854 Pope Pius IX Martin Luther learned the news: The Pope is infallible... not always of course, but only when he broadcasts the teachings of the church ex cathedra... First Vatican Council 1869 John Calvin learned the news: The Pope is infallible... not always of course, but only when he broadcasts teaching of the church ex cathedra... First Vatican Council 1869
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION