观察者模式——对象行为型模式
1、意图
对象之间存在某种依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都得到通知并做出相应反应。
2、适用性
在以下任一情况下可以使用观察者模式:
- 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
- 当对一个对象的改变需要同时改变其它对象,而不知道具体有多少对象有待改变。
- 当一个对象必须通知其它对象,而它又不能假定其它对象时谁。
3、结构
4、参与者
- 抽象目标(Subject)
- 目标知道它的观察者。可以有任意多个观察者观察同一个目标。
- 提供注册和删除观察者对象的接口。
- 抽象观察者(Observer)
- 为那些在目标发生改变时需获得通知的对象定义一个更新接口。
- 具体目标(ConcreteSubject)
- 将有关状态存入各ConcreteObserver对象。
- 当它的状态发生改变时,向它的各个观察者发出通知。
- 具体观察者(ConcreteObserver)
- 维护一个指向ConcreteSubject对象的引用。
- 存储有关状态,这些状态应与目标的状态保持一致。
- 实现Observer的更新接口以使自身状态与目标的状态保持一致。
5、协作
-
当ConcreteSubject发生任何可能导致其观察者与其本身状态不一致的改变时,它将通知它的各个观察者。
-
在得到具体目标的改变通知后,ConcreteObserver可向目标对象查询信息,ConcreteObserver使用这些信息以使它的状态与目标对象的状态一致。
6、效果
观察者模式的一些优缺点:
- 目标与观察者之间的抽象耦合
目标对象仅仅知道实现了某个接口的一些对象,并不清楚它们属于那些具体类.这种耦合是最低的。
- 支持广播通信
通知被自动广播到所有已向该目标对象登记的对象,目标对象并不关心到底有多少对象对自己感兴趣,它唯一的责任就是通知它的观察者,这样给你在任何时刻添加或删除观察者的自由。
- 意外的更新
因为一个目标并不知道其观察者的存在,它可能对更新观察者的代价一无所知,在目标上一个看似无害的状态更新可能导致其观察者的出乎意料的连锁反应,而这种反应可能是有害的,并且这种错误是很难检查出来的。
7、实现
-
创建目标到观察者之间的映射
A ) 在目标对象中显式地保存观察者的引用;
B ) 在目标对象中使用一个关联查找机制,如Hash表.
-
观察多个目标
目标对象可以简单地将自己作为Update操作的一个参数,让观察者知道应去检查哪一个目标。
-
谁来触发更新
A) 由目标对象负责:当它的状态设定操作在改变目标对象的状态后自动调用notify().这种方式的优点是客户的负担轻;缺点是多个连续的操作会导致连续的更新,效率会低.
B) 让客户负责调用notify():其优点是客户可以在一系列状态改变完成后再一次性地触发更新,避免了很大不必要的操作;其缺点是客户的责任加重,客户可能忘记这个责任!
8、代码示例
class Subject;
//抽象观察者
class Observer {
public:
virtual ~Observer();
virtual void Update(Subject * theChangedSubject) = 0;
protected:
Observer();
};
//抽象目标
class Subject {
public:
virtual ~Subject();
virtual void Attach(Observer*);
virtual void Detach(Observer*);
virtual void Notify();
protected:
Subject();
private:
list<Observer*> *_observers;
};
void Subject::Attach(Observer* o) {
_observers->push_back(o);
}
void Subject::Detach(Observer* o) {
_observers->remove(o);
}
void Subject::Notify() {
for (list<Observer*>::iterator it = _observers->begin();it!=_observers->end();++it) {
(*it)->Update(this);
}
}
//具体目标
class ClockTimer :public Subject {
public:
ClockTimer();
virtual int GetHour();
virtual int GetMinute();
virtual int GetSecond();
void Tick();
};
void ClockTimer::Tick() {
Notify();
}
//具体观察者
class DigitalClock:public Observer {
public:
DigitalClock(ClockTimer*);
virtual ~DigitalClock();
virtual void Update(Subject*);
virtual void Draw();
private:
ClockTimer* _subject;
};
//当实例化一个新对象时,就在目标对象中添加该对象
DigitalClock::DigitalClock(ClockTimer* s) {
_subject = s;
_subject->Attach(this);
}
//当销毁该对象时,就在目标对象中销毁该对象
DigitalClock::~DigitalClock() {
_subject->Detach(this);
}
//当收到目标对象的通知时,改变自身状态
void DigitalClock::Update(Subject* theChangedSubject) {
if (theChangedSubject == _subject) {
Draw();
}
}
void DigitalClock::Draw() {
int hour = _subject->GetHour();
int minute = _subject->GetMinute();
//draw the digital clock
}
//具体另一个观察者
class AnalogClock :public Observer {
public:
AnalogClock(ClockTimer*);
virtual ~AnalogClock();
virtual void Update(Subject*);
virtual void Draw();
private:
ClockTimer* _subject;
};
AnalogClock::AnalogClock(ClockTimer *s) {
_subject = s;
_subject->Attach(this);
}
AnalogClock::~AnalogClock() {
_subject->Detach(this);
}
void AnalogClock::Update(Subject * theChangedSubject) {
if (theChangedSubject == _subject) {
Draw();
}
}
void AnalogClock::Draw() {
int hour = _subject->GetHour();
int minute = _subject->GetMinute();
//draw the analog clock
}
int main() {
//创建一个AnalogClock和DigitalClock,它们总是显示相同时间
ClockTimer* timer = new ClockTimer;
AnalogClock *analogClock = new AnalogClock(timer);
DigitalClock *digitalClock = new DigitalClock(timer);
}