JavaRush /Java Blog /Random-JA /オブザーバーテンプレート

オブザーバーテンプレート

Random-JA グループに公開済み
Gang of Four が書いているように (一流の開発者 4 人による書籍『オブジェクト指向デザイン パターン』を参照)、このパターンの目的は、オブジェクト間の 1 対多の依存関係を定義することです。 1 つのオブジェクトの状態が変化すると、そのオブジェクトに依存するすべてのオブジェクトにそのことが通知され、自動的に更新されます。 このパターンは、Dependents (従属) または Publish-Subscribe (パブリッシャー - サブスクライバー) とも呼ばれます。しかし、カトリック教会の例を使ってそれを理解してみましょう:) この教会には、この教会の教えを信じる信者がいます。新しい教義(義務的信条)などが現れたとき、これらの人々はそれについて知っておく必要があります。 しかし、このパターンを使用してプログラミング言語でこれをどのように記述することができるでしょうか? 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. 教会の教区民についても同じことができます。つまり、更新メソッドを別のインターフェイスに移動し、目的の教区民に対して実装します。そうすれば、この方法は、カトリック教会の教区民ではなく、たとえばエルフの存在の中で生きる人々(「ユニコーンへの道」運動を意味します)によって一般に使用できるようになります。それらの。update メソッドを使用して 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. com/en/java/javase/15/docs/api/java.base/java/util/Observable.html): 非推奨になりました。このクラスと Observer インターフェイスは非推奨になりました。Observer と Observable でサポートされるイベント モデルは非常に限定されており、Observable によって配信される通知の順序は指定されておらず、状態の変化は通知と 1 対 1 に対応していません。より豊富なイベント モデルについては、java.beans パッケージの使用を検討してください。スレッド間で信頼性が高く順序付けられたメッセージングを行うには、java.util.concurrent パッケージ内の同時データ構造の 1 つを使用することを検討してください。リアクティブ ストリーム スタイルのプログラミングについては、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 год");
}
次の結果が得られます。
マルティン・ルーサーはそのニュースを知りました: 聖母マリアは無原罪の御宿りです...雄牛イネファビリス・デウス... 1854 年 12 月 8 日 教皇ピウス 9 世ヨハネ・カルヴァンはそのニュースを知りました: 聖母マリアは無原罪の御宿りです...雄牛イネファビリス・デウス... 1854年12月8日 教皇ピウス9世マルティン・ルターはそのニュースを知った: 教皇は無謬である...もちろんいつもではないが、大聖堂の外の教会の教えを放送するときだけ...第一バチカン公会議1869年ジョン・カルヴィンは学んだニュース: 教皇は無謬である...もちろんいつもではないが、それは彼が大聖堂の外の教会の教えを放送するときだけだ...第一バチカン公会議1869年
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION