现实生活中,这样的例子太多了,一个对象的状态受另外一个对象的影响。比如,进度条根据上传的百分比而变化,红灯停绿灯行。。。。。这样的业务数不胜数。甚至我们有时候心情也是随着很多经历而变化。在开发过程中,这样的业务当然也是很多的,但是,稍有不慎,我们可能会实现出比较麻烦的代码。而设计模式中有一种模式对于解决这样的业务具有很好作为,那就是观察者模式。简而言之就是:我们有一个目标,还有很多观察者,当目标变化,观察者们将作出相关的变化。
观察者模式:
看看书上的定义,也是行为模式
意图:
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变,所有依赖于它的对象都得到通知并将自动更新。
对于这样一种业务,我们很自然的需要两种角色:目标和观察者
确实,为了遵循"开闭"原则,我们两种对象创建两种对象的抽象基类,这样便于扩展和维护。于是就有四个基本的结构。
抽象目标,抽象观察者和具体目标和具体观察者。
书上给出的结构图:
对这个结构图,需要注意到是目标的 Notify()和 观察者的 update()两个方法。
抽象代码可以实现为:
抽象目标:
class AbtSubject { private: vector<AbtObserver *> _obArr; //抽象目标有观察者
public: virtual void addObserver(AbtObserver *observer); //自己实现 virtual void rmObserver(AbtObserver *observer); //要实现 virtual void notify(); } void AbtSubject::notify() { for(auto obs:_obArr) { obs.update(); } }
注:虚析构和构造函数未给出。
抽象观察者:
class AbtObserver { protect: AbtObserver(); virtual ~AbtObserver(); public: virtual void update()=0; }
具体目标,假如是红绿灯:
enum RGcolor
{ red, green, yellow } class RGLight:public AbtSubject { private: RGcolor _color; public: RGcolor getColor() { return _color; } RGcolor setColor(RGcolor color) { _color = color; }
void colorChange()
{
notify();
}
}
简单的实现:
具体观察者:
#include<iostream> //如果有必要,引入subject头文件 class WalkingPerson() { private: AbSubject * _subject; public: void update(); void walk(); void stop(); } void WalkingPerson::update() { switch(_subject->getColor()) { case red: this->stop(); break; case green: this->walk(); break; } }
这样具体思路也就是这样,当然还有很多细节,比如构造函数的实现,这可能还得通过具体业务额定。至少到这里我们已经知道这样四个角色的含义了。
抽象目标:
自然的包含了一系列的观察者,定义了一些抽象接口,比如通知函数notify,用来通知自己拥有的那一系列观察者。和一些后代接口,比如添加删除观察者。
抽象观察者:
定义如何反应目标的通知的抽象方法update()
具体的目标:
具体实现。
具体观察者:
通过包含自己观察者来保存与目标的状态。
这样的一个结构很明显的好处就是:
1.目标和观察者耦合度最小:因为都是建立在两个抽象类之上,通过多态特性,可以把两种不同类之间的耦合度达到最小,可以动态 添加具体观察者而不影响其他观察者。
2.广播优势:也就是说发出信号而无需知道观察者是谁,一视同仁。
缺陷也很明显:
确实存在一种依赖关系,这种依赖关系可能导致未知影响,甚至是对于观察者的依赖对象的变化。还有目前的目标假设在一个,有时候存在多种目标,而需要观察者知道是哪个目标。
好处可以通过举返利,比如将person类耦合到目标类中,但是这样的违背了面向对象的原则,这里就不举例了。
设计模式:可复用面向对象软件的基础 这本书上还给了一些在实际开发过程中额外的复杂的依赖关系。
1."创建目标到其观察者之间的映射":这是作者假设如果目标过多而观察者比较少,额外的空间开销显得没有必要,所以通过映射(hash)方式来处理会更好,但是也可能造成观察者的开销。
2."观察多个观察者",我们上面的例子是只有一个subject,如果有多个,我们可能还需要将具体suject作为update的参数,这样观察者可以以此判断是什么目标发生变化。
3."判断是谁的更新",这个嘛,看了很久,上面例子中,我是在红绿灯颜色发生变化的时候调用的notify方法,也就是书上的第一个方案,有具体subject自己决定什么时候调用notify通知观察者,这种的坏处是可能存在效率问题。第二种,是有客户(使用者)自己去调用,这种方式坏处是增加客户责任,同时使用者的对要求也比较高。
4."对已经删除目标悬挂引用",就是说如果有个目标对象被删除,如何处理其相关观察者引用这个被删除对象的问题。
5."发出通知前确保目标状态的一致性",在发送通知的时候很可能目标和观察者直接的状态并不统一。
6."避免特定于观察者的更新协议——推/拉模型",推模型:广播由目标向观察者发送改变的信息。拉模型:有观察者自己询问改变信息。
7."显示指定感兴趣的信息",可以在添加的时候讲观察者感兴趣的信息告诉目标,更新的时候就传给目标。
8."封装复杂的更新",当目标和观察者之间的依赖关系过于复杂,应该建立一个中间对象来处理这种复杂的关系,书上给出了结构图。
可以看到,在目标和观察之间建立了一个manage对象。在理解过程中对一些业务其实还处在朦胧阶段,我想也许在将来的工作中,可以加深对该模式的理解,共勉。