观察者模式(Observer)
动机(Motivation)
在软件构建过程中,我们需要为某些对象建立一种 “通知依赖关系 ” ——一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好的抵御变化。
使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。
模式定义
定义对象间的一种一对多(变化)的依赖关系,以便当一个对象(Subject)的状态发生改变时,所有依赖于它的对象都得到通知并自动更新 ——《设计模式》GoF
结构(Structure)
实例
场景
想象如下一个场景: 一群码农正在摸鱼,但又害怕老板突然到来,于是非常小心,不停需要抬头看向门口。 于是形成一种观察与被观察的关系,摸鱼码农需要观察门口是否有老板到来的情况。
这样就很头疼,摸鱼不能好好摸,担惊受怕的。 于是大家决定,选出一个’哨兵’,站在门口,当出现事件时(老板来了),就通知他们,这样就可以放心 摸鱼。
这样关系进行了转换,现在大家只需要等待哨兵的信号即可。而哨兵则要专心关注门口的动静,以便及 时通知大家。
代码实现
class Subject;
class Observer//抽象观察者
{
protected:
string m_name;
Subject* m_sub;
public:
Observer(const string& name, Subject* sub)
:m_name(name), m_sub(sub)
{
cout << "Create Observer" << endl;
}
~Observer()
{
cout << "Destroy Observer" << endl;
}
virtual void update() = 0;
};
class Programmer : public Observer
{
public:
Programmer(const string& name, Subject* sub)
:Observer(name, sub) {
cout << "Programmer() " << endl;
}
~Programmer() { cout << "~Programmer()" << endl; }
void update()
{
cout << m_name << " 接收到信息 " << endl;
if (m_sub->hasChanged())
{
cout << m_sub->getMess() << " 停止摸鱼, 认真工作中..." << endl;
}
else
{
cout << " 老板没有来上班 ! " << m_sub->getMess() << endl;
}
}
};
class Subject//抽象被观察者,建立与观察之间的关系,提供notify接口
{
protected:
std::list<Observer*> _obslist; //
bool _is_boss; // true ; false;
std::string _mess;
public:
Subject() :_is_boss(false), _mess("摸鱼!") {}
~Subject() { detachAll(); }
void setChanged()
{
_is_boss = true;
_mess = "老板来了!";
}
void clearChanged()
{
_is_boss = false;
_mess = "老板没有来了!";
}
bool hasChanged() const { return _is_boss; }
const string& getMess() const { return _mess; }
void attach(Observer* obs)
{
_obslist.push_back(obs);
}
void detach(Observer* obs)
{
_obslist.remove(obs);
}
void detachAll()
{
_obslist.clear();
}
virtual void notify() = 0;
};
class Guard : public Subject
{
public:
Guard() {
cout << "create guard" << endl;
}
~Guard()
{
cout << "destroy guard" << endl;
}
void notify()
{
for (auto& x : _obslist)
{
x->update();
}
}
};
观察者模式的使用场景和优缺点
使用场景
关联行为场景,需要注意的是,关联行为是可拆分的,而不是“组合”关系。
事件多级触发场景。 跨系统的消息交换场景,如消息队列、事件总线的处理机制。
优点
实现了观察者和被观察者之间的抽象耦合。 动态联动
广播通信。被观察者会向所有的登记的观察者发出通知。
缺点
可能会引起无谓的操作。
由于采用广播方式,不管观察者需不需要,每个观察者都会被调用update方法 在应用观察者模式时需要考虑一下开发效率和运行效率的问题,程序中包括一个被观察者、多个观察 者,开发、调试等内容会比较复杂,在进行并发环境下,观察者卡顿,会影响整体的执行效率,在这种 情况下,一般会采用异步实现。
要点总结
- 使用面向对象的抽象,Observer模式使得我们可以独立的改变目标与观察者,从而使二者之间的依赖关系达到松耦合
- 目标发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播
- 观察者自己决定是否需要订阅通知,目标对象对此一无所知
- Observer模式是基于事件的UI框架中非常常用的设计模式,也是MVC模式的一个重要组成部分