読者です 読者をやめる 読者になる 読者になる

プログラミング C# - 翔ソフトウェア (Sho's)

C#/.NET/ソフトウェア開発など

C# による Observer パターンの実装 その2 - event による実装

Observer

前回「C# による Observer パターンの実装 その1 - 古典的な実装」と云う記事で、Observer パターンの C# による実装の第一回として、古典的な実装を行ってみた。

interface と抽象クラスを用いた実装だった訳だが、こうした場合、C# では event を用いるのが普通だろう。

今回は、「C# による Observer パターンの実装 その2」として、event による実装を行ってみる。

C# での Observer パターンの実装 2 - event による実装 1

・クラス図 1

クラス図はこんな感じ。

「C# での Observer パターンの実装 2 - event による実装 1」のクラス図
C# での Observer パターンの実装 2 - event による実装 1」のクラス図

前回 と比べるとフレームワーク部 (抽象部) が無くなっているのが判る。

C# が持っている event が強力なので、それだけで更新によって呼ばれるべき処理が呼べてしまうのだ。

Employee は Model として Number と Name と云う二つのプロパティを持っている。

また、Employee は Update というイベントを持っている。
Employee は更新されると Update イベントを起こす。

Update イベントが起きたときに、もしそのイベント ハンドラーがあれば、それが呼ばれる。

EmployeeView は Employee を表示するための View で、DataSource として Employee が渡されると、Employee の Update イベントにイベント ハンドラーとして Update メソッドを設定する。

EmployeeView は、表示部として TextControl を二つ持っていて、それぞれに DataSource の Number と Name を表示する。

・実装 1

それでは、実装していこう (一部エラー処理を省略)。

// C# による Oberver パターンの実装 - event による実装 1
using System;
using System.Collections.Generic;

// アプリケーション部

// Model: 更新を監視される側
class Employee
{
    public event Action<Employee> Update; // 更新イベント

    int number = 0;
    string name = string.Empty;

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

    public string Name
    {
        get { return name; }
        set {
            if (value != name) {
                name = value;
                if (Update != null)
                    Update(this);
            }
        }
    }
}

class TextControl // テキスト表示用のUI部品 (ダミー)
{
    public string Text {
        set { Console.WriteLine("TextControl is updated: {0}", value); }
    }
}

class EmployeeView // Employee 用の View: 更新を監視する側
{
    TextControl numberTextControl = new TextControl(); // Number 表示用
    TextControl nameTextControl = new TextControl(); // Name 表示用

    public Employee DataSource
    {
        set {
            value.Update += Update; // データソースの更新イベントにハンドラを設定
        }
    }

    void Update(Employee employee) // データソースのどのプロパティが更新されてもここに来る
    {
        numberTextControl.Text = employee.Number.ToString();
        nameTextControl.Text = employee.Name;
    }
}

class Program
{
    static void Main()
    {
        var employee = new Employee(); // Model, Observable
        var employeeView = new EmployeeView(); // View, Observer

        employeeView.DataSource = employee; // データバインド
        employee.Number = 100; // Number を変更。employeeView に反映されたかな?
        employee.Name = "福井太郎"; // Name を変更。employeeView に反映されたかな?
    }
}
・実行結果

実行してみると、次のようになる。

前回と同じ結果だ。

TextControl is updated: 100
TextControl is updated:
TextControl is updated: 100
TextControl is updated: 福井太郎

今回も矢張り、Employee の Number や Name が更新される度に、EmployeeView の Update が呼ばれ、二つの TextControl の表示が更新される。

C# での Observer パターンの実装 2 - event による実装 2

上では、Number と Name のどちらが更新されても両方の TextControl が更新されている。

これは、Number と Name のどちらが更新されても同じイベントが起きるようになっている為だ。

更新イベントを分けて、それぞれの TextControl が更新されるように改良してみよう 。

・クラス図 2

クラス図はこうなる。

「C# での Observer パターンの実装 2 - event による実装 2」のクラス図
C# での Observer パターンの実装 2 - event による実装 2」のクラス図

一つだった Employee 側の Update イベントが、UpdateNumber と UpdateName に分かれている。

・実装 2

それでは、実装してみよう (一部エラー処理を省略)。

// C# による Oberver パターンの実装 - event による実装 2
using System;
using System.Collections.Generic;

// アプリケーション部

// Model: 更新を監視される側
class Employee
{
    public event Action<Employee> UpdateNumber; // Number 更新イベント
    public event Action<Employee> UpdateName; // Name 更新イベント 

    int number = 0;
    string name = string.Empty;

    public int Number
    {
        get { return number; }
        set {
            if (value != number) {
                number = value;
                if (UpdateNumber != null)
                    UpdateNumber(this);
            }
        }
    }

    public string Name
    {
        get { return name; }
        set {
            if (value != name) {
                name = value;
                if (UpdateName != null)
                    UpdateName(this);
            }
        }
    }
}

class TextControl // テキスト表示用のUI部品 (ダミー)
{
    public string Text {
        set { Console.WriteLine("TextControl is updated: {0}", value); }
    }
}

class EmployeeView // Employee 用の View: 更新を監視する側
{
    TextControl numberTextControl = new TextControl(); // Number 表示用
    TextControl nameTextControl = new TextControl(); // Name 表示用

    public Employee DataSource
    {
        set {
            // それぞれの更新イベントでそれぞれ TextControl を更新
            value.UpdateNumber += employee => numberTextControl.Text = employee.Number.ToString();
            value.UpdateName += employee => nameTextControl.Text = employee.Name;
        }
    }
}

class Program
{
    static void Main()
    {
        var employee = new Employee(); // Model, Observable
        var employeeView = new EmployeeView(); // View, Observer
    
        employeeView.DataSource = employee; // データバインド
        employee.Number = 100; // Number を変更。employeeView に反映されたかな?
        employee.Name = "福井太郎"; // Name を変更。employeeView に反映されたかな?
    }
}
・実行結果

実行してみると、次のようになる。

Number と Name それぞれの更新で、それぞれの TextControl が更新されている。

TextControl is updated: 100
TextControl is updated: 福井太郎

それぞれで更新されるようになったのは良いが、Employee 側でプロパティ毎にイベントを用意しなければいけない。

これではプロパティの数が増えてくると、コードが複雑になりそうだ。

■ 今回のまとめと次回の予告

さて、Observer パターンの C# による実装の第二回として、C# の event を用いた実装を行ってみた。

前回のようなフレームワーク部がない。

次回は、フレームワーク部を復活させてみる。