たくあんポリポリ

勉強したことを載せていきます。最近、技術系の記事はZennに書いています。(https://zenn.dev/chittai)

【C#】実装から学ぶObserverパターン

Observerパターンについて整理しました。・・・というよりも、下記サイトにある情報を整理して自分なりに理解しやすい(特に実装を理解しやすい)ように噛み砕いてみました。(読み直してみたらわかりにくいのでそのうちもっとわかりやすく書き直したい)

目的は?

Observerパターンについて調べるのですが、毎回忘れてしまうのでちゃんと整理したいなと思ってます。ただ、"Observerパターンはこういう目的があって~"とか"こうしう仕組みで~"というよりも更に一段階下げて実装コードベースで整理したいと思ってます。(実装はよく見るけど、コードレベルでどういう流れか見ているものはあまりないかな~と思い)

さっそくObserverパターンとは

プログラム内のオブジェクトのイベント( 事象 )を他のオブジェクトへ通知する処理で使われるデザインパターンの一種。

Wikipediaより

監視される側と監視する側がいて、監視される側の持つ値が変わったらそれを監視する側に通知して、監視する側で定義している処理を実行してくれます。イメージはつきますが、実装がわからないとふわっとしたままで終わってしまうので実装から理解したいなと思いました。

クラス図 / 実装

参考サイトをご覧ください。サイトから情報をとってくると、抽象化部分は下記のようになります。

抽象化部分の整理

監視"する"側-インタフェース

IObserver
 +Update
監視"される"側の持つ値が変わったときにこのUpdateが呼び出される。

監視"される"側-抽象クラス

Observable
 +Add
 +Update
 +NotifyObservers
監視"する"側と"される"側のヒモ付をする。値が変わったら監視"する"側のUpdateを呼び出す。

実装の整理

上記をベースにプログラムをなぞっていきたいと思います。個人的にはまず、"通知"という表現がわかりにくいかなと思っているので、もう少し砕きたいなと思ってます。なので、監視"される"側のクラスで定義されたプロパティが更新された際に、setの中で更新通知用の関数を呼び出していると表現します。

static void Main()
{
    var employee = new Employee();  // Observable(監視"される"側)
    var employeeView = new EmployeeView(); // Observable(監視"される"側)

    employeeView.DataSource = employee;
    employee.Number = 100; 
    employee.Name = "TestNsme"; 
}

実行プログラムです。

 employeeView.DataSource = employee;

上からなぞっていくと、上記表現が最初に出てきます。これが、監視"される"側と監視"する"側を紐付ける処理になります。なので、このDataSoueceを見てみましょう。

public Employee DataSource
{
    set { value.Add(this); }
}

ここのvalueは監視"される"側です。つまり、監視"される"側のAddメソッドを呼び出しています。

List<IObserver> observers = new List<IObserver>();

public void Add(IObserver observer)
{
    observers.Add(observer);
}

Addメソッドはobserversというリストに監視"する"側のオブジェクトを登録しています。
つまり、ここまでで「DataSourceというプロパティに監視"される"側のインスタンスをセットしようとすると、実際には監視"される"側が持つリストに監視"する"側の情報を登録していることになります。ここで監視"される"側⇔監視"する"側のひも付ができます。

ここで登録をできれば、あとはどのように"通知"しているのかです。

public int Number
{
    get { return number; }
    set
    {
        if (value != number) {
            number = value;
            Update();★
        }
    }

これが、監視"される"側のプロパティです。なので、監視"される"側のUpdateを呼び出していることになります。では、Updateの中身はどうなっているのでしょうか。

protected void Update()
{
    NotifyObservers();
}

この様にNotifyObserversを呼び出す仕組みになっています。では、NotifyObserversは?

void NorifyObservers()
{
    observers.ForEach(observer => observer.Update(this));
}

ここで、observersが出てきています。さっきの監視"する"側の情報が登録されています。つまり、すべての監視"する"側のオブジェクトが持つUpdateメソッドを呼び出します。このUpdate内に処理を書くことで値の変更→通知→処理が実行されます。

つまり???

個人的にここさえ覚えれば細かいことを忘れても問題ないと思うのは、

  1. まずは、監視"される"側と監視"される"側を紐づけましょう→リストへの登録
  2. 値を変更しましょう→値を変更したらプロパティの中で、監視"する"側のUpdateまで呼び出すようにしましょう(実際は違うけど、考え的にはこういった表現にした)

この辺りとクラス図を照らせば理解しやすいかなと思いました。