JavaRush /Java Blog /Random-KO /관찰자 템플릿

관찰자 템플릿

Random-KO 그룹에 게시되었습니다
Gang of Four가 쓴 것처럼(4명의 최고 개발자가 쓴 "객체 지향 디자인 패턴"이라는 책 참조) 이 패턴의 목적은 다음과 같은 방식으로 객체 간의 일대다 종속성을 정의하는 것입니다. 한 개체의 상태가 변경되면 해당 개체에 종속된 모든 개체에 이에 대한 알림이 전달되고 자동으로 업데이트됩니다. 이 패턴은 종속 항목(하위 항목) 또는 게시-구독(게시자 - 구독자)이라고도 합니다. 하지만 가톨릭 교회의 예를 사용하여 알아 내려고합시다 :) 이 교회의 가르침을 믿는 추종자들이 있습니다. 새로운 교리(의무적 신조) 등이 나타나면 이 사람들은 그것에 대해 알아야 합니다. 하지만 이 패턴을 사용하는 프로그래밍 언어에서는 이를 어떻게 설명할 수 있을까요? 1. 우리에게는 “교회의 목소리”(교회 자체 또는 대성당에서 방송할 때는 교황), 즉 교회에서 소식을 알리는 특정 방송사나 주체가 있습니다. 2. 이 교회의 교구민, 즉 중요한 사건을 알고 싶어하는 관찰자가 있습니다. 따라서 오늘은 13억 명의 교구민이 있을 수 있고 내일은 그 수가 더 많거나 적을 수도 있습니다. 그리고 이 교회에 있는 사람들에게만 알리면 됩니다(무신론자들을 다시 귀찮게 할 필요는 없습니다 :). 따라서 이 모든 것은 다음과 같이 표현될 수 있습니다. 당신이 등록하거나 반대로 떠날 수 있는 것에 대해 양 떼에게 말할 교회가 있습니다.
public interface Church {
    void registerParishioner(Parishioner parishioner);
    void removeParishioner(Parishioner parishioner);
    void notifyParishioners();
}
이러한 방법을 구현하는 특정 가톨릭 교회가 있으며, 뉴스와 이 뉴스를 방송해야 하는 사람들의 목록도 있습니다.
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);
    }
}
교회에 들어가거나 나갈 수 있는 교구민이 있습니다. (코드를 단순화하기 위해 그 사람만 입장하도록 허용하겠습니다 :)
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);
    }
}
따라서 작동 방식은 다음과 같습니다.
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 года");
    }
}
그리고 프로그램의 결과는 다음과 같습니다.
Мартин Лютер узнал новость: Инквизиция была ошибкой... месса Mea Culpa 12 марта 2000 года
Жан Кальвин узнал новость: Инквизиция была ошибкой... месса Mea Culpa 12 марта 2000 года
저것들. 교회에 소식이 올라오자마자, 이 교회에 등록된 모든 회원들에게 그 소식이 통보됩니다. 이 구현의 단점은 무엇입니까? 1. 첫째, 등록하고 뉴스를 받을 수 있는 인터페이스는 이 교회뿐만 아니라 필요할 수도 있습니다. 따라서 이를 별도의 Observable 인터페이스로 즉시 이동하는 것이 가능할 것입니다. 2. 교회 교구민에게도 동일한 작업을 수행할 수 있습니다. 즉, 업데이트 방법을 별도의 인터페이스로 이동하고 원하는 교구민에 대해 구현합니다. 그러면 이 방법은 가톨릭 교회의 교구민이 아니라 예를 들어 엘프의 존재 속에 사는 사람들(“유니콘으로 가는 길” 운동을 의미)이 일반적으로 사용할 수 있게 될 것입니다. 저것들. 업데이트 메소드를 사용하여 Observer 인터페이스를 생성합니다. 결국에는 무슨 일이 일어날까요?
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);
    }
}
따라서: 우리는 교회와 교구민 사이의 "연결을 약화"시켰는데, 이는 프로그래밍에서만 당연히 좋습니다 :) 주제(가톨릭 교회)에는 청취자(교구민) 목록만 있고 뉴스(변경 사항)를 수신할 때 이 뉴스를 방송합니다. 청취자에게. 이제 다른 개체(예: 개신교 교회)를 만들고 "귀하의" 청취자에게 뉴스를 방송할 수 있습니다. 또한 클래스 2 데이터(보다 정확하게는 Observable 클래스 및 Observer 인터페이스)가 java.util Java 패키지에서 사용 가능했지만 이제는 Java 9(https://docs.oracle.dll)에서는 더 이상 사용되지 않는다는 점을 고려해야 합니다. com/en/java/javase/15/docs/api/java.base/java/util/Observable.html): 더 이상 사용되지 않습니다. 이 클래스와 Observer 인터페이스는 더 이상 사용되지 않습니다. Observer와 Observable이 지원하는 이벤트 모델은 매우 제한적이며 Observable이 전달하는 알림의 순서는 지정되지 않으며 상태 변경은 알림과 일대일 대응이 아닙니다. 보다 풍부한 이벤트 모델을 위해서는 java.beans 패키지 사용을 고려해보세요. 스레드 간 안정적이고 정렬된 메시징을 위해 java.util.concurrent 패키지의 동시 데이터 구조 중 하나를 사용하는 것을 고려하십시오. 반응형 스트림 스타일 프로그래밍의 경우 Flow API를 참조하세요. 그러므로 그것들을 사용할 필요가 없습니다. 대신 다른 것을 사용할 수도 있지만 패턴의 본질은 변하지 않습니다. 예를 들어, java.beans 패키지에서 (이미 작성된 불필요한 클래스를 작성하지 않도록) PropertyChangeListener를 사용해 보겠습니다. 그것이 어떻게 될지 봅시다: 주제 클래스:
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;
    }
}
리스너 클래스는 다음과 같습니다.
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);
    }
}
다음 코드를 실행하면:
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 год");
}
우리는 다음과 같은 결과를 얻습니다.
마틴 루터는 그 소식을 알게 되었습니다: 성모 마리아는 무염시태... 황소 Ineffabilis Deus... 1854년 12월 8일 교황 비오 9세 존 칼빈은 다음 소식을 알게 되었습니다: 성모 마리아는 무염시태... Bull Ineffabilis Deus ... 1854년 12월 8일 교황 비오 9세 마르틴 루터는 다음과 같은 소식을 알게 되었습니다: 교황은 무오하다... 물론 항상 그런 것은 아니지만, 그가 대성당에서 교회의 가르침을 방송할 때만... 1869년 제1차 바티칸 공의회 존 칼빈은 배웠습니다 뉴스: 교황은 무오류입니다... 물론 항상 그런 것은 아니지만, 대성당 밖에서 교회의 가르침을 방송할 때만... 1869년 제1차 바티칸 공의회
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION