C#でGoFのデザインパターン~Observerパターン編~
2019-10-30
azblob://2022/11/11/eyecatch/2019-10-30-csharp-gof-observerpattern-000.jpg

はじめに

GoFのデザインパターンについて、前回はStrategyパターンをご紹介しました。

このシリーズでは、「デザインパターンについて最もシンプルな例で動くものを作る!」ということを目標にして記事を作成しています。

実際にコードを書きながら、説明を理解したりコードを書き換えて動作を観察したりして、デザインパターンへの理解を深めていってください。

Observerパターンとは

というわけで、今回はObserverパターンの解説です。

Observerとは、日本語では動詞Observe(観察する、監視するとか)を行う人を意味しています。

プログラム上においては、一つのクラスとほかの複数のクラスを結び付け、一つのクラスで何かしらをトリガーすることで、結び付けられた残りのクラス全てにおいて特定の動作を実行させたい・・・という場面で使われます。

よくある例えが、パブリッシャー(発行者)とサブスクライバー(購読者)です。

サブスクライバーはパブリッシャーに対して、「あなたの発行するものを購読したいです」と言います。

するとパブリッシャーはそのサブスクライバーをリストに登録しておき、あとで何かしらを発行する際にはリストにあるサブスクライバー全員に発行したものを届ける、というものです。

単純なイメージとしては新聞の購読のようなものです。

早速実装

早速Observerパターンを実装してみましょう。

まずは、Observeをする側の振る舞いを規定するインターフェースを作成します。

    public interface IObserver
    {
        void update(int arg1, int arg2, int arg3);
        void show();
    }

中身は、オブザーバで持つパラメータのアップデートを行うものと、その値を表示するためのshow()の振る舞いを規定しています。

次に、その振舞いを実装していきます。

クラスの中に、update()で受け取った値を格納するparamを持ち、show()においてはそれらを表示します。

    class Observer1 : IObserver
    {
        private int param1;
        private int param2;
        private int param3;
        public void update(int arg1, int arg2, int arg3)
        {
            param1 = arg1;
            param2 = arg2;
            param3 = arg3;
        }
        public void show()
        {
            Console.WriteLine($"param1は{param1},param2は{param2},param3は{param3}です。");
        }
    }

ここまでできたら、これらのObserverを纏めるSubjectクラスを作成していきます。

これは、パブリッシャーとサブスクライバーの関係でいうところのパブリッシャーの側で、RegisterObserver()ではObserverのリストに新しいサブスクライバーを追加し、RemoveObserver()ではそれを取り除いたりできるようにしています。

また、NotifyObserver()においてはobserverのリストに登録されているサブスクライバーそれぞれに対してparamの値を「発行」しています。

また、ShowAll()においては、全部のサブスクライバーに同じshow()メソッドを実行するように指示を出しています。

    class Subject
    {
        private List<IObserver> observers;
        private int param1;
        private int param2;
        private int param3;

        public Subject()
        {
            observers = new List<IObserver>();
        }

        public void RegisterObserver(IObserver o)
        {
            observers.Add(o);
            Console.WriteLine("Observerを登録しました。");
        }

        public void RemoveObserver(IObserver o)
        {
            observers.Remove(o);
            Console.WriteLine("Observerを登録解除しました。");
        }
        public void NotifyObservers()
        {
            foreach (var observer in observers)
            {
                observer.update(param1, param2, param3);
            }
            Console.WriteLine("Observerのパラメータをアップデートしました。");
        }
        public void SetParams(int arg1, int arg2, int arg3)
        {
            param1 = arg1;
            param2 = arg2;
            param3 = arg3;
        }
        public void ShowAll()
        {
            foreach (var observer in observers)
            {
                observer.show();
            }
        }
    }

最後に、ここまで作ってきたクラスをProgram.csから呼び出します。

subjectにObserverを登録し、値を登録して発行し、表示させています。

    class Program
    {
        static void Main(string[] args)
        {
            var subject = new Subject();
            subject.RegisterObserver(new Observer1());
            subject.SetParams(1, 2, 3);
            subject.NotifyObservers();
            subject.ShowAll();
            subject.RegisterObserver(new Observer1());
            subject.NotifyObservers();
            subject.ShowAll();
        }
    }

ここまでうまくいっていれば、出力には以下の7行が表示されるはずです。

Observerを登録しました。
Observerのパラメータをアップデートしました。
param11,param2は2,param3は3です。
Observerを登録しました。
Observerのパラメータをアップデートしました。
param11,param2は2,param3は3です。
param11,param2は2,param3は3です。

Observerを追加で登録すると、show()が呼ばれるサブスクライバーも2つになるのが6、7行目で確認できます。

おわりに

今回はObserverパターンについて解説し、実装してみました。

Observerパターンは、インスタンス間に1対多の関係を結び、情報をやり取りしたりObserveしているオブジェクトを纏めて一気に更新したりするのに使えるパターンになります。

MVCパターンを構成する一部としても利用されているので、このパターンでのアプリケーション開発に慣れている人は実はなじみの深いものになります。

MVCパターンとGOFのデザインパターンの間の関係はまだほかにもトピックがあるので、気になる方はぜひ調べてみてください!