【デザインパターン】【GoF】【C#】Observerパターン

Observerパターン

状態の変化を通知する

ポイント

* インターフェイス(下記の例では、IObserver)および更新したいデータ群のクラス(MoneyData)を用意する。なお、インターフェイスには、このクラスを引数にもつ更新用のメソッド(Update(MoneyData moneyData))を追加する。
* 通知したいクラス(Form1, Form2)に、上記のインターフェイスを継承させる。Update()には更新されてくるデータが来るので、その結果を反映させる処理を実装しておく
* 更新させるクラス(Task)に、インターフェイスのリスト(IList<IObserver> observers)を用意しておき、通知したいクラスのインスタンスをリストに追加しておく。そのインスタンスの更新メソッド(observer.Update())に対して、データを通知する

サンプル

IObserver.cs

* このインターフェイスがキモ!!!
public interface IObserver
{
    void Update(MoneyData moneyData);
}

MoneyData.cs

public class MoneyData
{
    public int Yen { get; set; }
    public int Euro { get; set; }
}

Task.cs

public class Task
{
    MoneyData money;
    IList<IObserver> observers;
    Random random = new System.Random();

    public Task(Form1 form1, Form2 form2)
    {
        this.money = new MoneyData();
        this.observers = new List<IObserver>
        {
            form1,
            form2,
        };

        this.random = new Random();
    }

    public void Execute()
    {
        foreach(var observer in this.observers)
        {
            observer.Update(this.FakeData());
        }
    }

    private MoneyData FakeData()
    {
        this.money.Yen = this.random.Next(50, 200);
        this.money.Euro = this.random.Next(1, 3);

        return this.money;
    }
}

Form1.cs

public partial class Form1 : Form, IObserver
{
    private const int IintervalTime = 1000;
    private bool IsAlive = false;
    private Series series1;
    private Series series2;
    private int count = 0;

    public Form1()
    {
        InitializeComponent();

        this.chart1.Series.Clear();

        string yenMarketText = "円相場";
        string euroMarketText = "ユーロ相場";

        this.series1 = new Series(yenMarketText);
        this.series1.ChartType = SeriesChartType.FastPoint;
        this.series2 = new Series(euroMarketText);
        this.series2.ChartType = SeriesChartType.FastPoint;

        ChartArea area = new ChartArea();
        area.AxisX.Minimum = 0;
        area.AxisX.Maximum = 100000;
        area.AxisY.Minimum = 0;
        area.AxisY.Maximum = 500;

        this.chart1.ChartAreas.Add(area);
        this.chart1.Series.Add(this.series1);
        this.chart1.Series.Add(this.series2);
    }

    public void Update(MoneyData moneyData)
    {
        this.series1.Points.AddXY(this.count, moneyData.Yen);
        this.series2.Points.AddXY(this.count, moneyData.Euro);
    }

    // Startボタン
    private void button1_Click(object sender, EventArgs e)
    {
        this.IsAlive = true;

        Form2 form2 = new Form2();
        form2.Show();

        Task task = new Task(this, form2);

        var time = 0;
        var oldTime = Environment.TickCount;

        while (this.Created)
        {
            if (!this.IsAlive)
            {
                form2.Close();
                return;
            }

            time++;
            if (oldTime + IintervalTime <= Environment.TickCount)
            {
                task.Execute();
                oldTime = Environment.TickCount;
                time = 0;
            }
            Application.DoEvents();
        }
    }


    // Stopボタン
    private void button2_Click(object sender, EventArgs e)
    {
        this.IsAlive = false;
    }
}

Form2.cs

public partial class Form2 : Form, IObserver
{
    public Form2()
    {
        InitializeComponent();
    }

    public void Update(MoneyData moneyData)
    {
        this.label1.Text = string.Format("1ドル:約{0}円", moneyData.Yen);
        this.label2.Text = string.Format("1ドル:約{0}ユーロ", moneyData.Euro);
    }
}

補足

* 今回のサンプルでは、Formに対して、通知したが、ログ出力するクラスなどに対しても使える



関連記事

Observerパターン ~IObserver / IObservable インターフェース~

* C#には、IObserver / IObservable インターフェースがある。詳細は以下を参照。
http://blogs.yahoo.co.jp/dk521123/23250342.html