观察者模式

  从哲学的角度来说,世间万物存在着普遍的联系,某一事物的变化影响着所有和它有关联的事务。在软件开发中也是如此,一个对象状态状态的变化会连带着改变与它相关对象的状态。

  

 

 

 

 

  自然界中的变化往往是顺其自然的,但软件设计中,这种对象之间相互的影响需要设计者用巧妙的设计模式体现出来。观察者模式就是一种试图将某一对象变化带来的影响高效地传递给其他对象的设计模式。  

  观察者模式,又叫发布-订阅模式,体现了这样一种思想。某个对象的改变会连带着改变其他对象,因此所有会受到影响的对象,就会自然地成为观察者(Observer),它们需要去观察变化有没有出现,从而做出调整。例如,顾客需要观察股票市场的涨停,求职者需要关注招聘信息的发布。在软件开发中,这种观察需要落到一个具体的对象上去,也就是主题(Subject)。主题可能是直接造成了变化的对象,也可能是与变化最密切相关的对象。主题的状态一旦改变,观察者需要及时知晓并作出相应的改变。了解了观察者模式的大致思想以后,我们可以来看一下观察者模式的UML。

  观察者模式中主要有四个角色:

  主题(subject):被观察的对象,消息(变化)的发送方

  观察者(observer):观察者,消息(变化)的接受方

  具体主题(concreteSubject):具体的被观察对象

  具体观察者(concreteObserver):具体的观察者

  这四个角色中,主题和观察者之间的关系是观察者模式的核心。观察者如何知道主题状态的改变呢?和任何一种通信方式一样,有两种可能的方式,一是观察者主动了解,二是主题主动发送相关信息。观察者模式采用的是第二种方式,背后的原因很简单,如果是主题主动发送消息,只需要在状态发生变化时,通知观察者,但如果是观察者主动去获取,它必须时时刻刻关注主题,而且观察者和主题是多对一的关系,这表示系统中有大量对象要进行这种轮询。因此,第二种方式消耗的系统资源要远少于第一种。 

  为了能让主题发消息给观察者,最直接了当的方式就是观察者成为主题的成员。主题中包含一个队列,存储着它的观察者。主题需要一系列接口维护这个观察者队列,包括添加观察者,删除观察者,变化发生时通知所有观察者。对应的,所有观察者需要保持一个统一的接口,当主题发送通知时,更新自己的状态。

  观察者模式的核心思想基本上就介绍到这里,最后来看一下示例代码。先看观察者和具体观察者:

 1 class observer{
 2 
 3 public:
 4     virtual void update() = 0;  // 更新函数,每个具体的观察者都需要实现自己的更新函数
 5     
 6 };
 7 
 8 class concreteObserver: public observer{
 9 
10 public:
11     void update();
12 
13 };
14 
15 void concreteObserver::update(){
16     cout << "concreteObserver更新了自己的状态" << endl;
17 }

   然后是主题和具体主题:

class subject{
public:
    list<observer*>L;
    virtual void getChange() = 0;    //获取变化的函数
    virtual void notifyAllObserver()=0;        //同时所有的观察者
    void addObserver(observer*o);    // 添加观察者
    void deleteObserver(observer*o); //删除观察者
    
};
class concreteSubject:public subject{

public:
    void notifyAllObserver();
    void getChange();
};

void subject::addObserver(observer*o){
    L.push_back(o);
}

void subject::deleteObserver(observer*o){
    L.remove(o);
}
void concreteSubject::notifyAllObserver(){
    for (auto ob : L){
        ob->update();
    }
}

void concreteSubject::getChange(){
    // 当变化发生时,通知所有观察者状态改变
    notifyAllObserver();
}