C#でデザインパターン「Observerパターン」

C# コンピュータ
C#

Observerパターンはオブジェクトの変化を通知する仕組みとのこと。

C#ではObserverパターン用にIObservable<T>とIObserver<T>というインターフェイスが用意されているので、これを使ったサンプルコードをGeminiに生成してもらいました。今回はこちらを眺めてみたいと思います。

using System;
using System.Collections.Generic;

// サブジェクトインターフェース
public interface IObservable<T>
{
    IDisposable Subscribe(IObserver<T> observer);
}

// オブザーバーインターフェース
public interface IObserver<T>
{
    void OnNext(T value);
    void OnError(Exception error);
    void OnCompleted();
}

// サブジェクトクラス
public class Subject<T> : IObservable<T>
{
    private List<IObserver<T>> observers = new List<IObserver<T>>();

    public IDisposable Subscribe(IObserver<T> observer)
    {
        observers.Add(observer);
        return new Unsubscriber<T>(observers, observer);
    }

    public void Notify(T value)
    {
        foreach (var observer in observers)
        {
            observer.OnNext(value);
        }
    }

    public void NotifyError(Exception error)
    {
        foreach (var observer in observers)
        {
            observer.OnError(error);
        }
    }

    public void NotifyCompleted()
    {
        foreach (var observer in observers)
        {
            observer.OnCompleted();
        }
    }
}

// 登録解除クラス
public class Unsubscriber<T> : IDisposable
{
    private List<IObserver<T>> observers;
    private IObserver<T> observer;

    public Unsubscriber(List<IObserver<T>> observers, IObserver<T> observer)
    {
        this.observers = observers;
        this.observer = observer;
    }

    public void Dispose()
    {
        if (observer != null && observers.Contains(observer))
        {
            observers.Remove(observer);
        }
    }
}

// オブザーバークラス
public class Observer<T> : IObserver<T>
{
    public void OnNext(T value)
    {
        Console.WriteLine($"Received value: {value}");
    }

    public void OnError(Exception error)
    {
        Console.WriteLine($"Received error: {error.Message}");
    }

    public void OnCompleted()
    {
        Console.WriteLine("Completed");
    }
}

// 使用例
public class Example
{
    public static void Main(string[] args)
    {
        var subject = new Subject<int>();
        var observer = new Observer<int>();

        subject.Subscribe(observer);

        subject.Notify(1);
        subject.Notify(2);
        subject.NotifyError(new Exception("An error occurred"));
        subject.NotifyCompleted();
    }
}

とりあえずコンソールプロジェクトを作り実行すると以下のような結果に成ります。

Received value: 1
Received value: 2
Received error: An error occurred
Completed

オブザーバー(Observerオブジェクト)が通知を受けるオブジェクトに成ります。
通知を受ける(呼び出される)メソッドOnNext,OnError,OnCompletedの3つ。
OnErrorがエラーの通知ですのでエラー処理。
OnCompletedは完了通知ですので、一連の流れの後処理。
ONextは一連の流れで次の値が来たことを通知しているようです。

IObservableを継承したサブジェクトクラスのオブジェクトで、オブザーバークラスのオブジェクトをSubscribe()メソッドで登録(購読)します。Subscribe()メソッドの戻り値はUnsubscriberオブジェクトで、こちらのオブジェクトを.Dispose()するとオブザーバーの登録(購読)が解除されます。
複数のオブザーバーを登録することを想定し、内部でList<>として保管されされています。

実装方法として、Subjectクラス内のメンバー変数で値を保持し、プロパティで値を代入、異なる値が代入された場合、Notify()メソッドを呼び出し、オブザーバーへ変更通知を流すような使い方が出来そうです。また、オブザーバー側は変更通知によって実行したい処理を記述するオブジェクトだと考えられます。

現状の理解度として、C#のコードを読むと処理内容は理解できるのですが、これを使わないといけないシュチュエーションが思いつかないあたり、学習がたりていない感じがします。

コメント