JavaRush /Java Blog /Random EN /Observer Pattern

Observer Pattern

Published in the Random EN group
According to the Gang of Four (meaning the book "Object-Oriented Design Patterns" by 4 first-class 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 dependent on it are notified about it 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 (compulsory dogmas) and not only appear, these people should be aware of them. But how could it be described by a programming language using this pattern? 1. We have a "voice of the church" (the church itself or the Pope when it broadcasts ex cathedra), i.e. a certain broadcaster or subject that announces the news in the church. 2. There are parishioners of this church, i.e. some observers who want to be aware of important events. Accordingly, today there may be 1.3 billion parishioners, and tomorrow more or less. And you need to notify only those who are in this church (no need to disturb atheists once again :). Thus, all this could be expressed as follows: There is a church that will broadcast to 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 needs to 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 person-parishioner who can enter or leave the bosom of 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, 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 output of the program:
Мартин Лютер узнал новость: Инквизиция была ошибкой... месса Mea Culpa 12 марта 2000 года
Жан Кальвин узнал новость: Инквизиция была ошибкой... месса Mea Culpa 12 марта 2000 года
Those. as soon as the 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 the parishioners of the church, namely, the update method should be moved to a separate interface and implemented for the desired parishioner. Then this method will be able to be used in general and not by the parishioners of the Catholic Church, but, for example, those who have lived in the existence of elves (meaning the movement "Road to the Unicorn"). 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 “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 these news to its listeners. You can now start any other entity (for example, a Protestant church) and broadcast the news to “your” listeners there. You also need to take into account that the data of 2 classes (more precisely, the Observable class and the Observer interface) were 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, you don't need to use them. And instead of them, you can use others, but the essence of the pattern will not change from this. For example, let's try to use the PropertyChangeListener (so as not to write extra classes that are already 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 was of the Immaculate Conception... Bull Ineffabilis Deus... December 8, 1854 Pope Pius IX John Calvin learned the news: The Virgin Mary was 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... The First Vatican Council 1869 John Calvin learned the news: The Pope is infallible... not always of course, but only when he broadcasts Church teaching ex cathedra... First Vatican Council 1869
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION