2014年09月21日

オブザーバー

Observer

「観察者」とも訳される。
振る舞いに関するパターンが使われる機会が減っていく中で、このオブザーバーパターンは良く見かけます。
DataBindingとかListenerなどもこのパターンの範疇です。

サンプルコード

ここでは、一番基本的な構成のサンプル実装しています。
本店が観察者(Observer)で、支店が観察される方(Observable)です。
支店の従業員が増える毎に本店に連絡が行きます。
なお、以下のサンプルでは実装していませんが、Observableが複数のObserverを持つことに対応するとか、ObserverからObservableへの通知なども可能です。

古典的実装
    // 観察者用インタフェース
    interface IObserver
    {
        void Update(Observable observable);
    }
    // 観察される側の抽象親クラス
    abstract class Observable
    {
        IObserver _observer;

        public void Subscribe(IObserver observer)
        {
            _observer = observer;
        }
        // 観察者に対し変更を通知する
        protected void Update()
        {
            _observer.Update(this);
        }
    }
    // 支店クラス
    class BranchOffice : Observable
    {
    // 支店名
        public string Name { get; set; }
        // 従業員数 この値が更新されると、それを通知する
        private int _count;
        public int MemberCount 
        { 
            get { return _count; }
            set { _count = value; Update(); }
        }
    }
    // 本店(観察者)
    class HeadOffice : IObserver
    {
        private Dictionary<string, int> _dic = new Dictionary<string, int>();

        public void Subscribe(Observable observable)
        {
            observable.Subscribe(this);
        }
        // 観察している対象に変更があると実行される
        public void Update(Observable observable)
        {
            var branch = observable as BranchOffice;

            if(_dic.ContainsKey(branch.Name))
            {
                _dic[branch.Name] = branch.MemberCount;
            }
            else
            {
                _dic.Add(branch.Name, branch.MemberCount);
            }

            DisplayAllMemberCount();
        }

        private void DisplayAllMemberCount()
        {
            Console.WriteLine(_dic.Values.Sum());
        }
    }



それらを使う側のコード
public class Tester
{
        static void Main(string[] args)
        {
            var headOffice = new HeadOffice();
            var shop1 = new BranchOffice() { Name = "shop1" };
            var shop2 = new BranchOffice() { Name = "shop2" };
            var shop3 = new BranchOffice() { Name = "shop3" };

            headOffice.Subscribe(shop1);
            headOffice.Subscribe(shop2);
            headOffice.Subscribe(shop3);

            shop1.MemberCount = 10;
            shop2.MemberCount = 15;
            shop3.MemberCount = 20;
        }
}
同じパターンをEventを使ったサンプル
内容はほぼ一緒ですが、Eventがマルチデリゲートに対応しているので複数の観察者を持つことができます。
Tester側のコードもそのまま使えます。
    public class BranchOffice
    {
        public event Action<BranchOffice> Update;

        public string Name { get; set; }

        private int _count;

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

    public class HeadOffice
    {
        private Dictionary<string, int> _dic = new Dictionary<string, int>();

        public void Subscribe(BranchOffice observable)
        {
            observable.Update += Update;
        }

        public void Update(BranchOffice observable)
        {
            var branch = observable as BranchOffice;

            if (_dic.ContainsKey(branch.Name))
            {
                _dic[branch.Name] = branch.MemberCount;
            }
            else
            {
                _dic.Add(branch.Name, branch.MemberCount);
            }

            DisplayAllMemberCount();
        }

        private void DisplayAllMemberCount()
        {
            Console.WriteLine(_dic.Values.Sum());
        }
    }
posted by RR at 19:42 | Comment(0) | デザインパターン | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

※ブログオーナーが承認したコメントのみ表示されます。