本エントリーは某社内で実施するデザインパターン勉強会向けの資料となります。
本エントリーで書籍「Java言語で学ぶデザインパターン入門」をベースに学習を進めますが、サンプルコードはC#に置き換えて解説します。
第1回 :Iteratorパターン
第2回 :Adapterパターン
第3回 :Template Methodパターン
第4回 :Factory Methodパターン
第5回 :Singletonパターン
第6回 :Prototypeパターン
第7回 :Builderパターン
第8回 :Abstract Factoryパターン
第9回 :Bridgeパターン
第10回:Strategyパターン
第11回:Compositeパターン
第12回:Decoratorパターン
第13回:Visitorパターン
第14回:Chain of Responsibilityパターン
第15回:Facadeパターン
第16回:Mediatorパターン
Observerパターンとは
観察対象(subject)の状態が変化すると通知を受けます。
通知を受けた観察者(observe)は、状態変化に応じて処理を実施します。
観察対象(subject)は通知した後、観察者(observe)がどういう処理をするか知る必要が無く、分離することが出来る。
クラス図
各クラスの役割
※サンプルプログラムでは自前でObserverインターフェイスを作っているが、
.NET FrameworkでサポートされたIObserverインターフェイスがあるため、サンプルプログラムはそれを使用します。 https://msdn.microsoft.com/ja-jp/library/ee850490(v=vs.110).aspx
名前 | 役割 |
---|---|
DigitObserver | 数字で表示するIObserverの実装クラス |
GraphObserver | 数を"*"で表示するIObserverの実装クラス |
NumberGenerator | 数を生成するオブジェクトを表す抽象クラス(Subject) |
RandamNumberGenerator | ランダムで数を生成するNumberGeneratorの具象クラス |
Program | 動作テスト用クラス |
DigitObserverクラス
IObserverインターフェイスの実装クラス。
生成した数字をコンソールに表示する。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ObserverPettern_Sample
{
public class DigitObserver : IObserver<NumberGenerator>
{
private NumberGenerator generator;
public DigitObserver(NumberGenerator generator)
{
this.generator = generator;
}
void IObserver<NumberGenerator>.OnCompleted()
{
Console.WriteLine("DigitObserver:" + generator.GetNumber().ToString());
}
void IObserver<NumberGenerator>.OnError(Exception error)
{
throw new NotImplementedException();
}
void IObserver<NumberGenerator>.OnNext(NumberGenerator value)
{
throw new NotImplementedException();
}
}
}
GraphObserverObserverクラス
IObserverインターフェイスの実装クラス。
生成した数だけ"*"をコンソールに表示する。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ObserverPettern_Sample
{
public class GraphObserver : IObserver<NumberGenerator>
{
private NumberGenerator generator;
public GraphObserver(NumberGenerator generator)
{
this.generator = generator;
}
void IObserver<NumberGenerator>.OnCompleted()
{
int Count = 0;
Count = generator.GetNumber();
Console.Write("GraphObserver:");
for (int i = 0; i < Count; i++)
{
Console.Write("*");
}
Console.WriteLine("");
}
void IObserver<NumberGenerator>.OnError(Exception error)
{
throw new NotImplementedException();
}
void IObserver<NumberGenerator>.OnNext(NumberGenerator value)
{
throw new NotImplementedException();
}
}
}
#NumberGeneratorクラス
数を生成するオブジェクトを表す抽象クラス
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ObserverPettern_Sample
{
public abstract class NumberGenerator:IObservable<int >
{
private IList Observers = new List<IObserver<NumberGenerator>>();
public void AddObserver(IObserver<NumberGenerator> _observer)
{
Observers.Add(_observer);
}
public void DelteteObserver(IObserver<NumberGenerator> _observer)
{
Observers.Remove(_observer);
}
public void NotifyObservers()
{
foreach (IObserver<NumberGenerator> _Observer in Observers)
{
_Observer.OnCompleted();
}
}
public abstract int GetNumber();
public abstract void Execute();
public IDisposable Subscribe(IObserver<int> observer)
{
throw new NotImplementedException();
}
}
}
#RandamNumberGenerator
ランダムで数を生成する、NumberGeneratorの実装クラス。
・数を生成するたびに観察者に通知する
・通知した後の観察者の処理は関与しない
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ObserverPettern_Sample
{
class RandamNumberGenerator : NumberGenerator
{
private Random GRandam = new Random();
private int Number = 0;
public override int GetNumber()
{
return Number;
}
public override void Execute()
{
for (int i = 0; i < 20; i++)
{
Number = GRandam.Next(50);
NotifyObservers();
}
}
}
}
#Program
動作テスト用のクラス
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ObserverPettern_Sample
{
class Program
{
private static void Main(string[] args)
{
NumberGenerator generator = new RandamNumberGenerator();
var observer1 = new DigitObserver(generator);
var observer2 = new GraphObserver(generator);
generator.AddObserver(observer1);
generator.AddObserver(observer2);
generator.Execute();
Console.ReadLine();
}
}
}
#プログラム実行結果
#所感
1.ObserverとSubjectを完全に分離している為、Subjectの処理を変えてもObserverに影響はない
例:数字生成を乱数(RandamNumberGenerator)から連番(NormalNumberGenerator)に変更した場合
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ObserverPettern_Sample
{
class Program
{
private static void Main(string[] args)
{
//NumberGenerator generator = new RandamNumberGenerator();
NumberGenerator generator = new NormalNumberGenerator();
IObserver observer1 = new DigitObserver();
IObserver observer2 = new GraphObserver();
generator.AddObserver(observer1);
generator.AddObserver(observer2);
generator.Execute();
Console.ReadLine();
}
}
}
2.今回の例は監視対象のSUBJECTからの通知を受けて処理をまわすPush型。
更新されたデータを引数(例の場合は"NumberGenerator")としてもらう為、実装クラスは常に引数を定義する必要がある
3.Observerという名前だが実態は監視対象から受動的に変更通知を受けている
4..NetFrameWorkのイベントハンドラーはObserverの考えが基礎になっている
5.今、ObserverパターンをベースとしたReactive Extensionsプログラミングが流行っているらしい。※勉強会で初めて知りました。。。