设计模式之观察者模式与其C++通用实现(上)

    
    设计模式之观察者(Observer)模式与其C++通用实现分上、中、下三篇。上篇详细讲解何为观察者模式以及其特点,并给出一个应用实例与其实现。中篇研究如何运用C++各种技术实现一个通用/万能的观察者模式。下篇讨论中篇所给出的实现可能遇到的问题及解决方案。

设计模式之观察者(Observer)模式与其C++通用实现(上)
                                                                                                          ——林石 2008-09-30
观察者模式

    意图:   
    定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
    结构

    优点
  • 目标(Subject)与观察者(Observer)间抽象耦合
  • 支持广播通信/通知
    缺点
  • 会带来意外更新问题
    示例
    考虑这样一个例子:想知道公司最新的MM情报吗?加入我们MM情报邮件组吧。您只需要向我们发送一封订阅邮件即可,我们会把最新的MM情报以电子邮件形式通知您。
    现在我们来一步一步实现。很明显,示例中关心MM情况的人物即为订阅者,我们以Subscriber表示这一类人。在定义Subscriber之前我们先定义一个MMStatus枚举,用以表示MM状态:
  1. enum MMStatus {Dining, Sleeping, Working};
这里定义了三个常量用以简单地模拟MM所处的状态。现在我们可以定义Subscriber类了:
  1. struct Subscriber
  2. {
  3.     virtual void action(MMStatus status) = 0;
  4.     virtual ~Subscriber() {}
  5. };
    有经验的读者知道,这样定义Subscriber表明它是个基类,也是个抽象类,或者称之为接口(java中就有 interface关键字)。之所以这样设计,是因为可能存在很多种类型的订阅者(Subscriber接口的子类),每种订阅者对MM的同一种状态可能会有不同的处理方式。这里把多种类型的订阅者抽象出相同的接口方法,也就是Subscriber定义的第3行。第4行虽然只是个空定义,但这是不可缺少的,防止在用基类指针指向子类而delete基类指针时子类的析构行为能正确调用。我们继续定义我们的目标类。没错,就是MM情报组,我们以 MMIntelligenceAgent类表示。
  1. class MMInteligenceAgent
  2. {
  3. public:
  4.     void subscribe(Subscriber &subscriber);
  5.     void desubscribe(Subscriber &subscriber);
  6. private:
  7.     void notifyAll(MMStatus status);
  8. private:
  9.     std::list<Subscriber*> subscribers_;
  10. };
    MMInteligenceAgent类有两个公有方法(4、5行),分别用以增加和移除一个订阅者。订阅者可能有多个,我们选择以链表存储之(9行)。当MM状态变更后,借助notifyAll方法,链表中的所有订阅者都会得到通知。
    目标类的实现很简单:
  1. void MMInteligenceAgent::subscribe(Subscriber &subscriber)
  2. {
  3.     subscribers_.push_back(&subscriber);
  4. }

  5. void MMInteligenceAgent::desubscribe(Subscriber &subscriber)
  6. {
  7.     subscribers_.erase(
  8.         std::remove(subscribers_.begin(), subscribers_.end(), &subscriber),
  9.         subscribers_.end());
  10. }
  11. void MMInteligenceAgent::notifyAll(MMStatus status)
  12. {
  13.     for (list<Subscriber*>::iterator it = subscribers_.begin();
  14.         it != subscribers_.end(); ++it) {
  15.             (*it)->action(status);
  16.     }
  17. }
    主要的基类及方法都写好了,接下来我们示例个具体Subscriber类:偷窃者。偷窃者一般在偷盗目标熟睡时比较容易下手,于是偷窃者可使用MM情报组提供的服务,以便知道MM何时在睡觉:
  1. struct Larcener : public Subscriber
  2. {
  3.     virtual void action(MMStatus status)
  4.     {
  5.         if (status == Sleeping) {
  6.             // steal something ...
  7.         }       
  8.     }
  9. };
    为了使代码更完整,我们可以为MMInteligenceAgent增加一个对MM的跟踪方法,当发现MM状态改变时发出通知。
  1. void MMInteligenceAgent::trace()
  2. {
  3.     ...
  4.     MMStatus status = ...;
  5.     notifyAll(status);
  6. }
    最后以一个调用示例作为此篇的结束:
  1. int main()
  2. {   
  3.     ...
  4.     Larcener l;
  5.     MMInteligenceAgent mia;
  6.     mia.subscribe(l);
  7.     mia.trace();
  8.     ...
  9.     mia.desubscribe(l);
  10.     ...
  11. }
    
    <未完,待续>