观察者模式:定义对象与对象之间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并更新。
大家在高中时,每到自习课时都异常活跃:玩手机、聊天、打牌。。。。。然而每每到最活跃的时候,班主任老师就来了,然后收手机、收扑克牌、还要训斥一顿。
为了能让同学有好的自习体验,今天张三同学站了出来,主动帮我们盯梢,如果老师来了就私发条消息给其他同学。其他同学要做的只需要在张三那里报个名,好让张三能通知到自己。
那么这一情形就是个典型的"观察者模型":
![](https://i-blog.csdnimg.cn/blog_migrate/7aa813a5475edc2ea9a8425395231458.png)
作为通知者:张三,他的状态关乎其他人的状态。如果老师来了,张三需要通知给登记过的同学,哪些同学也就能及时收起手机、扑克进入到学习状态。
所以通知者需要能:增加、删除观察者对象,通知观察者改变状态,改变通知者自己的状态。
class Observer;
/* 通知者接口 */
class Subject{
public:
// 添加被通知者
virtual void Attach(Observer* obs) {}
// 移除被通知者
virtual void Detach(Observer* obs) {}
// 通知登记的同学
virtual void Notify() {}
// 设置通知者状态接口
void SetState(string s){
action = s;
}
// 获取通知者状态接口
string GetState(){
return action;
}
private:
string action;
};
作为其他同学(观察者),需要在收到通知者的通知时更新自己的状态。
/* 观察者接口 */
class Observer{
public:
Observer(string name, Subject* sub)
:_name(name)
, _sub(sub)
{}
// 更新到学习状态
virtual void Update(){}
public:
string _name;
Subject* _sub;
};
/* 哨兵类 */
class Sentry : public Subject{
public:
void Attach(Observer* obs){
obs_list.push_back(obs);
}
void Detach(Observer* obs){
obs_list.remove(obs);
}
void Nofity();
private:
// 登记需要被通知的同学
list<Observer*> obs_list;
};
/* 玩手机的同学类 */
class MobilePhone :public Observer{
public:
MobilePhone(string name, Subject* sub)
:Observer(name, sub)
{}
void Update(){
cout << _sub->GetState() << _name << "收起手机,开始学习!!!" << endl;
}
};
void Sentry::Nofity(){
list<Observer*>::iterator it;
for (it = obs_list.begin(); it != obs_list.end(); it++)
(*it)->Update();
}
如果张三在盯梢的时候,打了个盹,那么通知各位同学的就是老师了,所以我们容易理解,在实现上观察者不能依赖某一具体类,而应该是一个抽象的通知者。所以我们提供的是一个通知者接口,而之后具体实现上可以是哨兵张三,也可以是老师、甚至校长,他们也可以有自己的方法,但作为通知者他们是一样的(通知学生,进入学习状态)。
同样作为观察者,也可能由不同的状态改变:玩手机 -> 学习;聊天 -> 学习; 打牌 -> 学习...。所以通知者在通知时,也应该是通知一个抽象的观察者。
客端调用:
int main()
{
// 哨兵张三
Sentry* zhangsan = new Sentry();
// 玩手机的同学
MobilePhone* mp1 = new MobilePhone("李四", zhangsan);
MobilePhone* mp2 = new MobilePhone("王五", zhangsan);
MobilePhone* mp3 = new MobilePhone("赵六", zhangsan);
zhangsan->Attach(mp1);
zhangsan->Attach(mp2);
zhangsan->Attach(mp3);
zhangsan->SetState("张三:老师来了!!! ");
zhangsan->Nofity();
return 0;
}
玩手机的同学就能收到,张三的通知了:
特点:
将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性,我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用带来不便。而观察者模式关键对象是主题Subject和观察者Observer,一个Subject 可以有任意数量的依赖它的 Observer,一旦 Subject 发生变化,所有 Observer 都可以得到通知,Subject 发出通知时并不需要知道谁是它的观察者,任何一个具体的观察者也不需要知道其他观察者的存在。
***使用情况 GOF 给出了以下使用观察者模式的情况:
- 当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
- 当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。
- 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望 这些对象是紧密耦合的。
本篇参照《大话设计模式》、《深入浅出设计模式》、《设计模式:可复用面向对象软件的基础》相关章节内容。