WinFormアプリで シンプルなコードからObserverパターンへ移行しよう

このチュートリアルでは、簡単な通知システムを作成し、それをより柔軟で拡張しやすい設計に改善する方法を学びます。まず、シンプルなコードを書き、その後、Observerパターンというデザインパターンを導入して、コードの設計を改善していきます。

ステップ1: シンプルなコードを作成する

まずは、以下のシンプルなコードを見てください。このコードは、ボタンをクリックするとリストボックスにメッセージを表示するものです。

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        listBox1.Items.Add("ボタンがクリックされました!");
    }
}

このコードは、シンプルで理解しやすいですが、他のコンポーネントがボタンのクリックに反応する必要がある場合、コードが複雑になりがちです。

ステップ2: 問題点を理解する

この設計には、以下のような問題があります:

  • 密結合: ボタンのクリックイベントが直接リストボックスのアイテムを更新するため、他の部分に通知を広げるのが難しいです。
  • 拡張性の問題: 新たなコンポーネントがボタンのクリックに反応する場合、それぞれのコンポーネントに対応するコードを追加する必要があります。

ステップ3: Observerパターンの基本を学ぶ

Observerパターンは、あるオブジェクトの状態が変化した際に、関連するオブジェクトに通知を送る仕組みを提供します。これにより、オブジェクト同士が疎結合になり、拡張性が向上します。

Observerパターンの構成は以下の通りです:

  • Subject: 通知を発行するオブジェクト(例:Button)。
  • Observer: 通知を受け取るオブジェクト(例:リストボックス)。

ステップ4: Observerパターンを適用する

それでは、シンプルなコードをObserverパターンを使って改善してみましょう。

IObserverインターフェースを作成する

まず、通知を受け取るためのインターフェースを作成します。

public interface IObserver
{
    void Update(string message);
}

ISubjectインターフェースを作成する

次に、通知を発行するためのインターフェースを作成します。

public interface ISubject
{
    void RegisterObserver(IObserver observer);
    void RemoveObserver(IObserver observer);
    void NotifyObservers(string message);
}

ConcreteSubjectクラスを作成する

ボタンのクリックイベントに対応するクラスを作成します。

public class ButtonNotifier : ISubject
{
    private List<IObserver> observers = new List<IObserver>();

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

    public void RemoveObserver(IObserver observer)
    {
        observers.Remove(observer);
    }

    public void NotifyObservers(string message)
    {
        foreach (var observer in observers)
        {
            observer.Update(message);
        }
    }

    public void ButtonClicked()
    {
        NotifyObservers("ボタンがクリックされました!");
    }
}

ConcreteObserverクラスを作成する

リストボックスを更新するためのObserverクラスを作成します。

public class ListBoxObserver : IObserver
{
    private ListBox listBox;

    public ListBoxObserver(ListBox listBox)
    {
        this.listBox = listBox;
    }

    public void Update(string message)
    {
        listBox.Items.Add(message);
    }
}

Form1クラスを更新する

最後に、Form1クラスをObserverパターンに合わせて更新します。

public partial class Form1 : Form
{
    private ButtonNotifier buttonNotifier;

    public Form1()
    {
        InitializeComponent();
        buttonNotifier = new ButtonNotifier();

        var listBoxObserver = new ListBoxObserver(listBox1);
        buttonNotifier.RegisterObserver(listBoxObserver);
    }

    private void button1_Click(object sender, EventArgs e)
    {
        buttonNotifier.ButtonClicked();
    }
}

ステップ5: 振り返りと応用

Observerパターンを導入したことで、ボタンのクリックに対する処理が拡張しやすくなりました。新しいコンポーネントを通知に追加する際も、既存のコードを変更する必要がありません。

振り返り

  • 柔軟な設計: 新しいObserverを追加する際に、既存のコードをほとんど変更せずに済むため、柔軟な設計が可能です。
  • コードの分離: 各Observerが独立したクラスに分かれているため、コードの見通しが良く、保守性が向上しました。

応用例

このパターンは、通知システムに限らず、他のオブジェクトがイベントに反応する必要があるシーンでも使えます。例えば、複数のウィジェットが一つのデータに依存する場合や、ログイン状態の変更を他のコンポーネントに通知する場合などにも応用できます。

このチュートリアルを通じて、シンプルな設計からデザインパターンを導入するプロセスを学び、より柔軟で拡張性のあるコードを書けるようになりましょう。