苏黎 搜索开发工程师

设计模式-Observer模式(一)


观察者模式(Observer)

最近在看《Linux多线程服务端编程》这本书的时候,书中拿了Observer模式做了一个例子。因为之前没有学习过该模式,然后就拿了设计对象这本书学习起来。看完之后很受启发,其中解决了我一个很大的困惑,15年在华为实习的时候有幸阅读了华为交换机(WLAN部门)的核心代码。当时只记得大致程序框架是程序内部有很多服务,这些服务由统一的服务管理(server manager)来进行管理,服务通过注册到管理器上,服务与服务之间通过UDP进行通信,服务和服务管理使用消息通信机制来进行触发和通知。整体实现了服务之间的解耦,同时服务又是一个单独的进程,即使服务崩溃,主系统一般不会崩溃。设计较为合理。一直以来不明白为什么这么设计?看了观察者模式后慢慢理解了,这种设计的好处。

1,设计的意图

定义了对象之间的一种一对多的依赖关系,其中当一个对象发生了改变之后,所有依赖于他的对象都会得到通知并且自动更新。

2,解决了什么问题

在开发大型系统中,通常都是会将一个大的系统分割为多个类或者模块,但是随之而来的问题是如何去维护这些类或者模块的一致性,假定不能很好地处理这个问题,将会带来系统的耦合性太高或者太过于复杂等问题。Observer模式专门用来解决这个问题。

3,适用那些场景

在什么时候使用观察者模式呢?

  • 当一个抽象的模型有两个方面,其中一个依赖于另一个方面,同时将这两个封装在独立的对象中以使他们可以独立的改变和复用。
  • 当一个对象的改变需要同时改变其他的对象,并且不知道有多少对象需要改变。
  • 当一个对象必须通知其他对象,而又不能假定其他对象是谁,另外一个说法就是,你为了让他们解耦合。

以上三种是观察者模式使用的三种场景。

4,使用效果

  • 目标合观察者之间的解耦,通过观察者模式可以实现目标和多个观察者之间的独立,观察者之间可以互相不知道对方的存在。同时目标和观察者可以属于不同的系统抽象层次,保持了多层次之间的完整性。
  • 支持广播通信,目标可以广播通知所有的观察者,观察者在收到通知后,自行判断是不是自己订阅的消息,自行进行处理。

5,Observer结构

1

图片引用地址

6,实现

本例子实现了一个单线程的Observer观察者模式代码。

  1. 定义一个Observer类
class Subject;
class Observer {
 public:
  virtual ~Observer(){};
  virtual void Update(Subject* ChangObserver)=0;//更新服务逻辑
 protected:
  Observer(){};
};

可以看到类中定义了一个Update的虚函数,用来 实现子类中需要更新的服务。

  1. 定义Subject订阅类,用于存放和记录和注册实例化的多个观察者。
class Subject{
public:
  virtual ~Subject(){};
  virtual void Attach(Observer*);//订阅和注册服务
  virtual void Detach(Observer*);//移除注册的服务
  virtual void Notify();//通知观察者
 protected:
  Subject(){};
 private:
  vector<Observer*> _observer;
};

Subject订阅类中有三个虚函数,分别是注册服务和移除服务,已经通知观察者。实现代码如下:

void Subject::Attach(Observer * observer)
{
  _observer.push_back(observer);
}

void Subject::Detach(Observer * observer)
{
  vector<Observer*>::iterator iter = _observer.begin();
  while (iter != _observer.end()){
    if((*iter) == observer)
      _observer.erase(iter);
  }
}

void Subject::Notify()
{
  vector<Observer*>::iterator iter = _observer.begin();
  for(;iter != _observer.end();iter++){
    (*iter)->Update(this);//全部更新消息
  }
}
  1. 使用Observer模式 前面讲到,对于观察者模式很适合服务模块化编程,对于多服务管理非常适合。下面有一个简单的使用。
    • 假设有一个click时钟每秒触发一下更新,则该click类是subject订阅类,使用如下:
class Click : public Subject  {
 public:
  Click(){}
  void SecondClick();
};

void Click::SecondClick()
{
  while(1){
    sleep(1);
    Notify();
  }
}
  • 创建多个Observe实例来注册到subject上,然后再统一更新这些观察者,让其打印自身的信息。实例如下:
class ObserverReceive1 : public Observer {//实例1
 public:
  ObserverReceive1(Click*);
  virtual ~ObserverReceive1();

  virtual void Update(Subject* );

  virtual void DisplayInfo();
 private:
  Click* m_click;
};

class ObserverReceive2 : public Observer{//实例2
 public:
  ObserverReceive2(Click*);
  virtual ~ObserverReceive2();

  virtual void Update(Subject*);

  virtual void DisplayInfo();
 private:
  Click* m_click;
};

ObserverReceive1::ObserverReceive1(Click *c)
{
  m_click = c;
  m_click->Attach(this);//注册自己;
}
ObserverReceive1::~ObserverReceive1() {
  m_click->Detach(this);
}
void ObserverReceive1::Update(Subject *change) {
  if(change == m_click){
    DisplayInfo();//显示打印信息
  }
}
void ObserverReceive1::DisplayInfo() {
  std::cout<<"I am ObserverReceive1 ,now I'm receive the update."<<std::endl;
}


ObserverReceive2::ObserverReceive2(Click* c){
  m_click = c;
  m_click->Attach(this);
}

ObserverReceive2::~ObserverReceive2(){
  m_click->Detach(this);
}
void ObserverReceive2::Update(Subject *change) {
  if(change == m_click){
    DisplayInfo();
  }
}
void ObserverReceive2::DisplayInfo() {
  std::cout<<"I am ObserverReceive2 ,now I'm receive the update."<<std::endl;
}

当每秒触发更新的时候每个Observer实例会打印自己的信息。运行效果如下图:

2

后记:

观察者模式可以很灵活使用,在写大型多组件多服务系统时候,观察者模式可以很好的进行分层,同时能够解耦合,解耦合很重要,后续还会对Observer模式进行详细介绍。

参考文献:

1,《设计模式》 2,http://www.cnblogs.com/wangjq/archive/2012/07/12/2587966.html


Similar Posts

Comments