C#でObserverパターンをきちんと理解して実装する

#Observerパターンとは

Observerパターンとは、「クラスから通知を発行する仕組み」と、「他のクラスから発行された通知を受け取る仕組み」を実現するためのデザインパターンです。
このデザインパターンは、主にクラス間のデータのやり取りをするときに使われますが、以下のような状況において特に有用です。

1. 双方向ではなく、一方からもう一方へと__一方通行でデータを発行する場合__。
- 1回ではなく、__複数回データを発行する場合__。
- __任意のタイミングでデータを発行したい場合__。
- __発行されたデータを、複数のクラスが同時に受け取りたい場合__。

特に1番はObserverパターンの必須要件で、双方向のデータのやり取りには使用できません。

#ObserverとObservable

このデザインパターンでは、「__データを受け取るクラス__」と「__データを発行するクラス__」を明確に分けて考えます。
このうち、「データを受け取るクラス」のことを「__Observer__」、「データを発行するクラス」のことを「__Observable__」と呼びます。

__Observer__とは、「__監視者__」や「__観察者__」といった意味の英単語で、データを発行するクラス（Observable）からの通知を受け取る（つまり、通知を観察する、監視する）ことからその名が付けられています。
対して__Observable__とは、Observerの-able系なので、「__観察可能__」といった意味になります。
Observableは「観察者」であるObserverにデータを発行するので、Observerから「観察されることができる」、「観察可能」といったことから、この名がつけられています。

ObserverとObservableは非常に重要なので、以下のようにまとめます。

|  | Observer | Observable |
|:-:|:-:|:-:|
|__単語の意味__| 観察者  | 観察可能<br>観察されることができる  |
|__クラスの意味__| 通知を受け取るクラス  | 通知を発行するクラス  |

#基本的な仕組み

Observerパターンの基本的な仕組みは、次の3ステップとなります。

1. ObserverがObservableにデータの発行先として登録する（＝購読する）
2. Observableは登録されたすべての発行先に値を発行する
3. Observerは受け取った値を使用して任意の処理を実行する

次の項から詳しく説明します。

##1. ObserverがObservableを購読する

Observer（値を受信したいクラス）は、欲しい値を発行しているクラス（Observable）に対して、「僕に値を発行・通知してください」と__自分自身を通知先として登録します__。
この「配信登録」することを、「__購読(Subscribe)する__」と言います。

現実世界で「AさんはS社の雑誌を定期購読する」といった表現はよく使われます。
この場合も、AさんはS社に対して「僕に雑誌を定期的に送ってください」と自分自身を送付先として登録しますね。それと同じような意味になります。

![Observerパターン.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/697830/7cd0c260-6028-69d2-ac12-e7501761ef8d.png)

そして、一つの値の発行元（Observable）に対して、複数のObserverが値の発行先として登録することができることも、Observerパターンの特徴の一つです。

![Observerパターン_複数登録.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/697830/fa4d96c0-4b91-47d9-5da5-16f44e809240.png)




##2.Observableは登録されたすべての発行先に値を発行する
Observable（値を発行するクラス）は、発行する値ができると、値の発行先として登録された__すべてのObserverに対して一斉に値を発行します__。
こうして、ObserverはObservableからの通知（値）を受け取ることができます。
![Observerパターン_値の発行.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/697830/0e06e3ed-815d-082f-9f80-28352388b520.png)

##3.Observerは受け取った値を使用して任意の処理を実行する

Observableから発行された値を受け取ったObserverは、受け取った値を使用して任意の処理を実行することができます。

このようにして、ObservableからObserverへとデータの受け渡しが実現されます。

#Observerパターンを実装してみる

Observerパターンの基本的な仕組みの大枠は理解頂けたかと思います。
それでは、これらの仕組みを実際にどのように実装するかを説明していきます。

##`IObserver<T>`インターフェイスと`IObservable<T>`インターフェイス

Observerパターンは、「データを受け取るクラス」と「データを発行するクラス」を明確に分けて考えるデザインパターンと書きました。
実は、.NET Framework4.0以降には、__Observerパターンで使える`IObserver<T>`インターフェイスと`IObservable<T>`インターフェイスが`System`名前空間に標準で用意されています__。
Observerパターンを実装したい場合、これらのインターフェイスをデータの受信側、データの発行側のクラスにそれぞれ実装すれば良いことになります。

これらのインターフェイスの定義は次のようになっています。

```csharp
public interface IObserver<in T>
{
    //データの発行が完了したことを通知する
    void OnCompleted();
    //データの発行元でエラーが発生したことを通知する
    void OnError(Exception error);
    //データを通知する
    void OnNext(T value);
}

public interface IObservable<out T>
{
    //データの発行を購読する
    IDisposable Subscribe(IObserver<T> observer);
}
```

`IObserver<T>`インターフェイスの`OnNext`メソッドは、__データを新しく通知する__際に呼び出すメソッドです。
`OnCompleted`メソッドは、データの発行がすべて完了し、__これ以上通知するものがない場合__に呼び出します。
万が一、データの発行元で__何らかの例外が発生してしまった場合__は、`OnError`メソッドを呼び出して、エラーが発生したことを通知します。

`IObservable<T>`インターフェイスの`Subscibe`メソッドは、__データの発行元からの通知を受け取りたいとき__に呼び出します。
引数には、通知の発行先となる`IObserver<T>`オブジェクトを指定します。



ここで、以下のような違和感を覚える方がいらっしゃるかもしれません。

_`IObserver<T>`はデータを受け取るクラスが実装するのに、なぜデータを発行する系のメソッドを実装するのか？
逆に`IObservable<T>`はデータを発行するクラスが実装するのに、なぜデータの発行を購読するメソッドを実装するのか？_

その答えは、これらのメソッドは、__実装するクラス自身が使用するのではなく、お互いに相手が使用するものだから__です。

例えば、「データを受け取るクラス(Observer)」が実装した`IObserver<T>`インターフェイスの`OnNext`メソッドは、データを発行するクラス(Observable)が呼び出すことで、Observerに対して値を通知することができます。
逆に、「データを発行するクラス(Observable)」が実装した`IObservable<T>`インターフェイスの`Subscribe`メソッドは、データを受け取るクラス(Observer)が呼び出すことで、自分自身をデータの発行先として登録することができます。

まだ違和感が拭えないかもしれませんが、実際に実装してみると理解が深まるかもしれません。
それでは、実際に`IObserver<T>`インターフェイスと`IObservable<T>`インターフェイスを用いて、Observerパターンを実装してみます。

##Observerを実装する

まずはObserver（通知を受け取るクラス）を作成します。

###1.クラスを作成する

```csharp
public class Observer
{

}
```

###2.`IObserver<T>`インターフェイスを実装する

このクラスはObserverなので、`IObserver<T>`インターフェイスを実装します。
型引数`T`には受信したい値の型を指定します。ここでは、`int`型とします。

```csharp
public class Observer : IObserver<int>
{
    public void OnCompleted()
    {
        throw new NotImplementedException();
    }

    public void OnError(Exception error)
    {
        throw new NotImplementedException();
    }

    public void OnNext(int value)
    {
        throw new NotImplementedException();
    }
}
```

###3.値を受け取ったときのコールバック処理を記述する

あとは、各メソッドにそれぞれ通知が来たときに実行したい処理を自由に記述します。
例なので、ここでは次のようにコンソールにメッセージを出力するだけの処理を実装しました。

```csharp
public class Observer : IObserver<int>
{
    public void OnCompleted()
    {
        Console.WriteLine($"通知の受け取りが完了しました");
    }

    public void OnError(Exception error)
    {
        Console.WriteLine($"次のエラーを受信しました:{error.Message}");
    }

    public void OnNext(int value)
    {
        Console.WriteLine($"{value}を受け取りました");
    }
}
```

これだけでObserverの実装は完了です。

###複数のObserverを用意できるようにする

冒頭にも書きましたが、Observerパターンでは、発行された値を複数のクラス（Observer）が同時に受け取ることができます。
これを試すために、次のようにObserverの名前をコンストラクタで指定するようにして、どのObserverがメッセージを受け取ったかを識別できるようにしておきます。

```csharp
public class Observer : IObserver<int>
{
    private string m_name;
    public Observer(string name)
    {
        m_name = name;
    }

    public void OnCompleted()
    {
        Console.WriteLine($"{m_name}が通知の受け取りを完了しました");
    }

    public void OnError(Exception error)
    {
        Console.WriteLine($"{m_name}が次のエラーを受信しました:{error.Message}");
    }

    public void OnNext(int value)
    {
        Console.WriteLine($"{m_name}が{value}を受け取りました");
    }
}
```

##Observableを実装する

次にObservable（通知を発行するクラス）を作成します。

###1.クラスを作成する

```csharp
public class Observable
{

}
```

###2.`IObservable<T>`インターフェイスを実装する

このクラスはObservableなので、`IObservable<T>`インターフェイスを実装します。
型引数`T`には発行したい値の型を指定します。ObserverとObservableの型引数は合致している必要があるため、ここでも`T`は`int`とします。

```csharp
public class Observable : IObservable<int>
{
    public IDisposable Subscribe(IObserver<int> observer)
    {
        throw new NotImplementedException();
    }
}
```

###3.値の発行先を覚えておく仕組みを作成する

Observableの役目は、値の発行先として登録された（購読された、`Subscribe`された）`IObserver<T>`を記憶しておき、発行する値が生じたときに、そのすべての`IObserver<T>`に対して値を発行することです。
※「値を発行する」とは、具体的には`IObserver<T>`の`OnNext`、`OnCompleted`、もしくは`OnError`メソッドを呼び出すことです

したがって、Observableなクラスに`Subscribe`で指定された`IObserver<int>`を覚えておける仕組みを作成する必要があります。
とはいっても、単に`List<IObserver<T>>`に溜めておくだけで十分です。

```csharp
public class Observable : IObservable<int>
{
    //購読されたIObserver<int>のリスト
    private List<IObserver<int>> m_observers = new List<IObserver<int>>();

    public IDisposable Subscribe(IObserver<int> observer)
    {
        if(!m_observers.Contains(observer))
            m_observers.Add(observer);
    }
}
```

これで、`Subscribe`で指定された`IObserver<int>`を覚えておくことができるようになりました。

###4.購読解除用の`IDisposable`なクラスを用意する

ところで、`Subscribe`メソッドの戻り値は`IDisposable`になっています。
今まで説明しませんでしたが、Observerパターンは、値の発行先として登録するSubscribeの対となる機能として、__値の発行を停止してもらう「購読解除」も可能となっています__。

__`Subscribe`メソッドの戻り値として返す`IDisposable`は、`Subscribe`したObserverが、「もう値はいらないです」と購読を解除するときに使用するものです__。
ご存知の通り、`IDisposable`インターフェイスの中身は`Dispose`メソッドただ一つのみであり、__購読を解除したいObserverはこの`IDisposable`を`Dispose`することによって購読を解除__することができます。

したがって、`Dispose`されたときに購読を解除する仕組みを作成する必要があります。
「購読を解除する」とは、Observableなクラス視点で言えば、値の発行先リストである`List<IObserver<T>>`から購読を解除したい`IObserver<T>`を`Remove`することに他なりません。

では、どのように実装すればよいでしょうか？

まず、`IDisposable`インターフェイスを返さなければなりませんから、当然、`IDisposable`インターフェイスを実装したクラスが必要になります。
そこで、以下のように「購読を解除する責務を持った`IDisposable`なクラス」を作成します。

```csharp
class Unsubscriber : IDisposable
{
    public void Dispose()
    {
        throw new NotImplementedException();
    }
}
```

そして、この`Dispose`メソッドの中に、購読者リストから`Remove`する処理を書けば完成です。
具体的には、発行先リスト`List<IObserver<T>>`と`Dispose`されたときに`Remove`するターゲットとなる`IObserver<T>`を、コンストラクタで引き渡して、以下のように実装します。

```csharp
class Unsubscriber : IDisposable
{
    //発行先リスト
    private List<IObserver<int>> m_observers;
    //DisposeされたときにRemoveするIObserver<int>
    private IObserver<int> m_observer;

    public Unsubscriber(List<IObserver<int>> observers, IObserver<int> observer)
    {
        m_observers = observers;
        m_observer = observer;
    }

    public void Dispose()
    {
        //Disposeされたら発行先リストから対象の発行先を削除する
        m_observers.Remove(m_observer);
    }
}
```

そして、この新しく作った購読解除用の`IDisposable`なクラスのインスタンスを、`Subscribe`の戻り値として返します。

```csharp
public class Observable : IObservable<int>
{
    //購読されたIObserver<int>のリスト
    private List<IObserver<int>> m_observers = new List<IObserver<int>>();

    public IDisposable Subscribe(IObserver<int> observer)
    {
        if(!m_observers.Contains(observer))
            m_observers.Add(observer);
        //購読解除用のクラスをIDisposableとして返す
        return new Unsubscriber(m_observers, observer);
    }
}
```

こうすることで、この`IDisposable`を受け取ったObserverは、値が要らなくなった時点で`Dispose`メソッドを呼び出すことで、自分自身を発行先リストから削除することができるようになります。

ところで、この`Unsubscriber`クラスは、`Observable`クラス以外から生成されることはありません。
したがって、下記のように`Observable`クラスの内部クラスにしてしまいます。

```csharp

public class Observable : IObservable<int>
{
    //購読されたIObserver<int>のリスト
    private List<IObserver<int>> m_observers = new List<IObserver<int>>();

    public IDisposable Subscribe(IObserver<int> observer)
    {
        if(!m_observers.Contains(observer))
            m_observers.Add(observer);
        //購読解除用のクラスをIDisposableとして返す
        return new Unsubscriber(m_observers, observer);
    }

    //購読解除用内部クラス
    private class Unsubscriber : IDisposable
    {
        //発行先リスト
        private List<IObserver<int>> m_observers;
        //DisposeされたときにRemoveするIObserver<int>
        private IObserver<int> m_observer;

        public Unsubscriber(List<IObserver<int>> observers, IObserver<int> observer)
        {
            m_observers = observers;
            m_observer = observer;
        }

        public void Dispose()
        {
            //Disposeされたら発行先リストから対象の発行先を削除する
            m_observers.Remove(m_observer);
        }
    }
}
```

###5.通知を発行する処理を記述する

最後に、発行したい情報や値があった場合に通知を発行する処理を記述します。
ここでは例として、`SendNotice`メソッドが呼ばれたときに“int”型の1,2,3を連続で通知を発行するようにします。

値を発行するには、`OnNext`メソッドを呼び出します。
すべての発行先に対して`OnNext`メソッドを呼び出すので、以下のように`foreach`を使用すればOKです。

```csharp
public class Observable : IObservable<int>
{
    //購読されたIObserver<int>のリスト
    private List<IObserver<int>> m_observers = new List<IObserver<int>>();

    public IDisposable Subscribe(IObserver<int> observer)
    {
        if(!m_observers.Contains(observer))
            m_observers.Add(observer);
        //購読解除用のクラスをIDisposableとして返す
        return new Unsubscriber(m_observers, observer);
    }

    public void SendNotice()
    {
        //すべての発行先に対して1,2,3を発行する
        foreach (var observer in m_observers)
        {
            observer.OnNext(1);
            observer.OnNext(2);
            observer.OnNext(3);
        }
    }

    //購読解除用内部クラス
    private class Unsubscriber : IDisposable
    {
        //発行先リスト
        private List<IObserver<int>> m_observers;
        //DisposeされたときにRemoveするIObserver<int>
        private IObserver<int> m_observer;

        public Unsubscriber(List<IObserver<int>> observers, IObserver<int> observer)
        {
            m_observers = observers;
            m_observer = observer;
        }

        public void Dispose()
        {
            //Disposeされたら発行先リストから対象の発行先を削除する
            m_observers.Remove(m_observer);
        }
    }
}
```

##使ってみる

これまで作成したObserverとObservableを使用して、実際に値の購読と発行、受け取りの流れを実演します。

ここでは、以下のようなコンソールアプリケーションを作成しました。

```csharp
class Program
{
    static void Main(string[] args)
    {
        //値を受け取るクラスを３つ作成
        Observer observerA = new Observer("Aさん");
        Observer observerB = new Observer("Bさん");
        Observer observerC = new Observer("Cさん");

        //値を発行するクラスを作成
        Observable observable = new Observable();

        //３つのObserverが、自分自身を発行先として登録する（=購読）
        IDisposable disposableA = observable.Subscribe(observerA);
        IDisposable disposableB = observable.Subscribe(observerB);
        IDisposable disposableC = observable.Subscribe(observerC);
        Console.WriteLine("Aさん〜Cさんが値を購読しました");

        Console.WriteLine("値を発行させます");
        //Observableに値を発行させる
        observable.SendNotice();
        
        Console.WriteLine("Aさんが購読解除します");
        //Aさんが購読解除する
        disposableA.Dispose();

        Console.WriteLine("値を発行させます");
        //再び値を発行させる
        observable.SendNotice();

        Console.WriteLine("Bさんが購読解除します");
        //Bさんが購読解除する
        disposableB.Dispose();

        Console.WriteLine("値を発行させます");
        //再び値を発行させる
        observable.SendNotice();

        Console.ReadKey();
    }
}
```

やっている内容としては、コードを見たとおりですが

1. 値の購読者（Observer）をAさん、Bさん、Cさんの３つを作成
2. 値の発行者（Observable）を作成
3. Aさん〜CさんがObservableを購読
4. Observableに値を発行させる
5. Aさん、購読を解除する
6. Observableに再び値を発行させる
7. Bさんも購読を解除する
8. Observableに再び値を発行させる

となります。

これを実行すると、次のような出力が得られます。

```
Aさん〜Cさんが値を購読しました
値を発行させます
Aさんが1を受け取りました
Aさんが2を受け取りました
Aさんが3を受け取りました
Bさんが1を受け取りました
Bさんが2を受け取りました
Bさんが3を受け取りました
Cさんが1を受け取りました
Cさんが2を受け取りました
Cさんが3を受け取りました
Aさんが購読解除します
値を発行させます
Bさんが1を受け取りました
Bさんが2を受け取りました
Bさんが3を受け取りました
Cさんが1を受け取りました
Cさんが2を受け取りました
Cさんが3を受け取りました
Bさんが購読解除します
値を発行させます
Cさんが1を受け取りました
Cさんが2を受け取りました
Cさんが3を受け取りました
```

Observerパターンが期待通りに動作していることが確認できます。

#さいごに

以上、少し長くなってしまいましたがObserverパターンの解説を行いました。

僕自身としては、Observerパターンをこのようにそのまま利用することは少なく、ReactiveExtensionsというObserverパターンをベースとした非常に強力なライブラリを通じて利用することがほとんどです。
もともとは、このReactiveExtensionsの記事を書いていたのですが、ベースとなるObserverパターンの説明がとても長くなってしまったため、別記事として抜き出しました。
またReactiveExtensionsの記事も書いたらリンクを貼るのでそちらも良ければ御覧ください。

[2020/12/22追記]
ReactiveExtensionに関する記事書きました。よろしければ参照ください。
__[【C#】なぜReactiveExtensionsを導入すると幸せになれるのか](https://qiita.com/yutorisan/items/2475eaf5d8c3a6454e3f#comments)__

何か間違いやご指摘、ご質問あればコメントにお願いします。