观察者模式(Observer)
最近在看《Linux多线程服务端编程》这本书的时候,书中拿了Observer模式做了一个例子。因为之前没有学习过该模式,然后就拿了设计对象这本书学习起来。看完之后很受启发,其中解决了我一个很大的困惑,15年在华为实习的时候有幸阅读了华为交换机(WLAN部门)的核心代码。当时只记得大致程序框架是程序内部有很多服务,这些服务由统一的服务管理(server manager)来进行管理,服务通过注册到管理器上,服务与服务之间通过UDP进行通信,服务和服务管理使用消息通信机制来进行触发和通知。整体实现了服务之间的解耦,同时服务又是一个单独的进程,即使服务崩溃,主系统一般不会崩溃。设计较为合理。一直以来不明白为什么这么设计?看了观察者模式后慢慢理解了,这种设计的好处。
1,设计的意图
定义了对象之间的一种一对多的依赖关系,其中当一个对象发生了改变之后,所有依赖于他的对象都会得到通知并且自动更新。
2,解决了什么问题
在开发大型系统中,通常都是会将一个大的系统分割为多个类或者模块,但是随之而来的问题是如何去维护这些类或者模块的一致性,假定不能很好地处理这个问题,将会带来系统的耦合性太高或者太过于复杂等问题。Observer模式专门用来解决这个问题。
3,适用那些场景
在什么时候使用观察者模式呢?
- 当一个抽象的模型有两个方面,其中一个依赖于另一个方面,同时将这两个封装在独立的对象中以使他们可以独立的改变和复用。
- 当一个对象的改变需要同时改变其他的对象,并且不知道有多少对象需要改变。
- 当一个对象必须通知其他对象,而又不能假定其他对象是谁,另外一个说法就是,你为了让他们解耦合。
以上三种是观察者模式使用的三种场景。
4,使用效果
- 目标合观察者之间的解耦,通过观察者模式可以实现目标和多个观察者之间的独立,观察者之间可以互相不知道对方的存在。同时目标和观察者可以属于不同的系统抽象层次,保持了多层次之间的完整性。
- 支持广播通信,目标可以广播通知所有的观察者,观察者在收到通知后,自行判断是不是自己订阅的消息,自行进行处理。
5,Observer结构
6,实现
本例子实现了一个单线程的Observer观察者模式代码。
- 定义一个Observer类
class Subject;
class Observer {
public:
virtual ~Observer(){};
virtual void Update(Subject* ChangObserver)=0;//更新服务逻辑
protected:
Observer(){};
};
可以看到类中定义了一个Update的虚函数,用来 实现子类中需要更新的服务。
- 定义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);//全部更新消息
}
}
- 使用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实例会打印自己的信息。运行效果如下图:
后记:
观察者模式可以很灵活使用,在写大型多组件多服务系统时候,观察者模式可以很好的进行分层,同时能够解耦合,解耦合很重要,后续还会对Observer模式进行详细介绍。
参考文献:
1,《设计模式》 2,http://www.cnblogs.com/wangjq/archive/2012/07/12/2587966.html