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

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

C# による Observer パターンの実装 その1 - 古典的な実装

Observer

Expression を使ってラムダ式のメンバー名を取得する」と云う記事で、ラムダ式の中のメンバーの名前を取得した。

これから数回に渡って、Observer パターンの C# による実装を何種類か例に挙げ、その中で上記を活用していきたい。

今回は、その第一回として、Expression どころか C# の event すら使わない、Observer パターンの最も古典的な実装を見てみよう。

■ Observer パターンとは

先ず Observer パターンの簡単な説明から。

Observer パターンは、GoF による 23 種のデザインパターンの中の一つで、特にポピュラーなものの一つだ。

・参考資料:

■ C# での Observer パターンの実装 1 - 古典的な実装

早速 C# で Observer パターンを実装していこう。

今回は、手始めに上の参考資料で紹介されているような由緒ある方法で実装してみる。

・クラス図

クラス図はこんな感じ。

「C# での Observer パターン 1 - 古典的な実装」のクラス図
「C# での Observer パターン 1 - 古典的な実装」のクラス図
・フレームワーク部 (抽象部)

上半分の青い部分がフレームワーク部分だ。

IObserver が Observable を監視している。

Observable の Add で IObserver を実装したオブジェクト (= オブザーバー) を追加する。

Observable は更新されると、以下の手順で全オブザーバーに更新があったことを通知してオブザーバーの表示が更新されるようにする。

  1. Update() を呼ぶ。
  2. Update() では NotifyObservers() が呼ばれる。
  3. NotifyObservers() は、全オブザーバーの Update() を呼ぶ。
  4. オブザーバーは、Update() の中で表示を更新する。
・アプリケーション部 (具象部)

下半分の赤い部分がアプリケーション部分だ。

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

EmployeeView がオブザーバーだ。
EmployeeView は Employee を表示するための View で、DataSource として Employee が渡されると、Employee のオブザーバーとして自分自身を追加する。

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

・実装

それでは、実装していこう。

一部エラー処理を省略する。

・フレームワーク部の実装

先に、フレームワーク部の IObserver と Observable から実装しよう。

こんな感じだ。

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

// フレームワーク部

interface IObserver // 更新を監視する側
{
    void Update(Observable observable);
}

abstract class Observable // 更新を監視される側
{
    List<IObserver> observers = new List<IObserver>();

    public void Add(IObserver observer) // オブサーバーの追加
    {
        observers.Add(observer);
    }

    protected void Update() // 更新イベント
    {
        NorifyObservers();
    }

    void NorifyObservers() // 全オブザーバーに更新を通知
    {
        observers.ForEach(observer => observer.Update(this));
    }
}
・アプリケーション部の実装

続いて、アプリケーション部。ここには、Employee と EmployeeView、TextControl、そして Main を含んだクラス Program がある。

// C# による Oberver パターンの実装 その1
using System;

// アプリケーション部

// Model
class Employee : Observable
{
    int number = 0;
    string name = string.Empty;

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

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

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

class EmployeeView : IObserver // Employee 用の View
{
    TextControl numberTextControl = new TextControl(); // Number 表示用
    TextControl nameTextControl = new TextControl(); // Name 表示用

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

    public void Update(Observable observable) // データソースのどのプロパティが更新されてもここに来る
    {
        var employee = observable as Employee;
        if (employee != null) {
            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 の表示が更新される。

Number の更新でも Name の更新でも同じ Update が呼ばれている。
ここは、出来ればそれぞれ処理したいところだ。

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

さて、Observer パターンの C# による実装の第一回として、古典的な実装を行ってみた。

interface と抽象クラスを用いてフレームワーク部を書いている。

次回は、もう少し C# らしい、event を用いた実装をやってみよう。