Design Pattern - Observer
(史帝芬, 2003/10/26, hi.steven@gmail.com)
  Observer的目的在於定義一對多的物件依存關係,讓物件狀態一有變動,就自動通知其他相依物件做該做的更 新動作,這個Pattern可以說是MVC的基礎架構。我自己第一次使用它是用在電子郵件認證,當系統收到使用者 回覆的email時,通知所有相關物件處理後續事項; 第二次使用是在電腦象棋中,當電腦決定出下一著手時,通 知棋盤、棋著列表和歷史棋著儲存區更新狀態。這是個常有機會用到的Pattern,學會了一定會受益良多。在 Observer裡的update是NumberGenerator用來通知Observer,內容已被更新的method。
  底下分別以C++、Java、C#來實作,各位網友可以看到同一個pattern在不同語言間仍會有些差異。

  • C++

  • NumberGenerator.h
    #ifndef __NUMBER_GENERATOR_H
    #define __NUMBER_GENERATOR_H
    
    #include  <set>
    
    #if _MSC_VER > 1020   // if VC++ version is > 4.2
     using namespace std;  // std c++ libs implemented in std
    #endif
    
    #include "Observer.h"
    
    typedef set<Observer*> SET;
    
    class NumberGenerator {
    private:
     SET _observers;
    public:
     virtual void attach(Observer*);
     virtual void detach(Observer*);
     void notify();
     virtual void setState(int n) = 0;
     virtual int getState() = 0;
    };
    
    #endif
    
    NumberGenerator.cpp
    #include "NumberGenerator.h"
    #include "Observer.h"
    
    void NumberGenerator::attach(Observer* o)
    {
     _observers.insert(o);
    }
    
    void NumberGenerator::detach(Observer* o)
    {
     _observers.erase(o);
    }
    
    void NumberGenerator::notify()
    {
     SET::iterator i;
     for(i = _observers.begin(); i != _observers.end(); i++) {
      ((Observer*)(*i))->update(this);
     }
    }
    
    Observer.h
    #ifndef __OBSERVER_H
    #define __OBSERVER_H
    
    class NumberGenerator;
    
    class Observer {
    public:
     virtual void update(NumberGenerator* n) = 0;
    };
    
    #endif
    
    RandomNumberGenerator.h
    #ifndef __RANDOM_NUMBER_GENERATOR_H
    #define __RANDOM_NUMBER_GENERATOR_H
    
    #include "NumberGenerator.h"
    
    class RandomNumberGenerator: public NumberGenerator {
    private:
     int state;
    public:
     int getState();
     void setState(int s);
    };
    
    #endif
    
    RandomNumberGenerator.cpp
    #include "RandomNumberGenerator.h"
    
    int RandomNumberGenerator::getState()
    {
     return state;
    }
    
    void RandomNumberGenerator::setState(int s)
    {
     state = s;
     notify();
    }
    
    DigitObserver.h
    #ifndef DIGIT_H
    #define DIGIT_H
    
    #include "Observer.h"
    class NumberGenerator;
    
    class DigitObserver: public Observer {
    public:
     void update(NumberGenerator* n);
    };
    
    #endif
    
    DigitObserver.cpp
    #include "DigitObserver.h"
    #include "NumberGenerator.h"
    #include <iostream>
    
    void DigitObserver::update(NumberGenerator* n)
    {
     cout << "state: " << n->getState() << endl;
    }
    
    GraphObserver.cpp
    #ifndef GRAPH_H
    #define GRAPH_H
    
    #include "Observer.h"
    class NumberGenerator;
    
    class GraphObserver: public Observer {
    public:
     void update(NumberGenerator* n);
    };
    
    #endif
    
    測試程式
    #include "RandomNumberGenerator.h"
    #include "DigitObserver.h"
    #include "GraphObserver.h"
    
    void main()
    {
     NumberGenerator* gen = new RandomNumberGenerator();
     Observer* ob1 = new DigitObserver();
     Observer* ob2 = new GraphObserver();
     gen->attach(ob1);
     gen->attach(ob2);
     gen->setState(5);
    }
    

    執行結果
    state: 5
    state: #####

    說明:
    在class NumberGenerator裡用到的set是STL。

  • Java

  • NumberGenerator.java
    import java.util.Vector;
    import java.util.Iterator;
    
    public abstract class NumberGenerator {
     private Vector observers = new Vector();
    	
     public void attach(Observer o) {
      observers.add(o);
     }
    	
     public void detach(Observer o) {
      observers.remove(o);
     }
    	
     public void Notify() {
      for(Iterator it = observers.iterator(); it.hasNext(); ) {
       Observer o = (Observer) it.next();
       o.update(this);
      }
     }
    	
     public abstract int getState();
     public abstract void setState(int n);
    }
    
    Observer.java
    public interface Observer {
     public void update(NumberGenerator n);
    }
    
    RandomNumberGenerator.java
    public class RandomNumberGenerator extends NumberGenerator {
     private int count = 0;
    	
     public int getState() {
      return count;
     }
    
     public void setState(int n) {
      count = n;
      Notify();
     }
    }
    
    DigitObserver.java
    public class DigitObserver implements Observer {
    
     public void update(NumberGenerator n) {
      int count = n.getState();
      System.out.println("state => " + count);
     }
    }
    
    GraphObserver.java
    public class GraphObserver implements Observer {
    
     public void update(NumberGenerator n) {
      int count = n.getState();
      System.out.print("state => ");
      for(int i=0; i < count; i++) {
       System.out.print("#");
      }
      System.out.println("");
     }
    }
    
    測試程式
    NumberGenerator gen = new RandomNumberGenerator();
    Observer ob1 = new DigitObserver();
    Observer ob2 = new GraphObserver();
    gen.attach(ob1);
    gen.attach(ob2);
    gen.setState(6);
    
    執行結果
    state => 6
    state => ######

  • C# (2008/07/28 新增)

  • C++及Java透過interface達到委託的目的,C#直接在語言中提供了delegate,在實現 observer pattern時更簡單方便。這兩種方法的差別最明顯有兩個:
    1. 透過interface,委託的標的物是class,透過delegate時,標的物為method。
    2. C#透過運算子的多載,使得委託函式的新增、移除更簡單。
    using System;
    
    namespace ObserverPattern
    {
        public class NumberGenerator
        {
            public delegate void Observer(int n);
            private Observer observer;
    
            public void attach(Observer o)
            {
                observer += o;
            }
    
            public void detach(Observer o)
            {
                observer -= o;
            }
    
            public void update(int n)
            {
                observer(n);
            }
        }
    
        class Program
        {
            public void DigitObserver(int n)
            {
                Console.WriteLine("State: " + n);
            }
    
            public void GraphObserver(int n)
            {
                Console.Write("State: ");
                for (int i = 0; i < n; i++)
                {
                    Console.Write("#");
                }
                Console.WriteLine();
            }
    
            static void Main(string[] args)
            {
                Program pgm = new Program();
    
                NumberGenerator num = new NumberGenerator();
                num.attach(pgm.DigitObserver);
                num.attach(pgm.GraphObserver);
                num.update(10);
                Console.ReadLine();
            }
        }
    }
    
    執行結果
    State: 10
    State: ##########