模式定义
观察者模式(Observer Pattern):定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。
模式动机
观察建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应做出反应。在此,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间没有相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展,这就是观察者模式的模式动机。
应用实例
-
在教室里老师还没有来,同学都在干着各的事情,小张正在打游戏,小李正在抄作业....., 现在同学们要求班长当卧底,监视老师,当老师来了通知大家一声。然后打游戏的马上停止,抄作业的也停止。
这里班长相当于是一个通知者,即具体目标, 小张、小李,以及其他同学显然是监听者即观察者,他们监听了班长那的消息,一旦老师来了马上采取相关的行动。 -
在打
Boss
时,会有很多人一起打,当Boss
血量为 0 时需要通知英雄停止攻击,而此处,英雄即为观察者,他们观察Boss
的状态,Boss
为观察目标。
模式结构
观察者模式包含如下角色:
- Subject: 目标
- ConcreteSubject: 具体目标
- Observer: 观察者
- ConcreteObserver: 具体观察者
代码分析
//抽象英雄, 即观察者
class AbstractHero {
public:
virtual void Update() = 0;
};
//具体英雄,即具体观察者
class HeroA :public AbstractHero {
public:
HeroA() { cout << "英雄A正在打Boss!" << endl; }
virtual void Update() { cout << "英雄A停止打Boss!" << endl; }
};
class HeroB :public AbstractHero {
public:
HeroB() { cout << "英雄B正在打Boss!" << endl; }
virtual void Update() { cout << "英雄B停止打Boss!" << endl; }
};
class HeroC :public AbstractHero {
public:
HeroC() { cout << "英雄C正在打Boss!" << endl; }
virtual void Update() { cout << "英雄C停止打Boss!" << endl; }
};
class HeroD :public AbstractHero {
public:
HeroD() { cout << "英雄D正在打Boss!" << endl; }
virtual void Update() { cout << "英雄D停止打Boss!" << endl; }
};
//抽象Boss,即目标
class AbstractBoss {
public:
virtual void AddHero(AbstractHero* hero) = 0; //添加观察者,即要给那些人通知
virtual void DelHero(AbstractHero* hero) = 0; //删除观察者,即取消一部分人的通知
virtual void Modify() = 0; //告诉观察者要更新了
};
//具体Boss,即具体观察目标
class BossA :public AbstractBoss {
public:
virtual void AddHero(AbstractHero* hero) { mHero.push_back(hero); }
virtual void DelHero(AbstractHero* hero) { mHero.remove(hero); }
virtual void Modify() {
for (auto it = mHero.begin(); it != mHero.end(); it++) {
(*it)->Update();
}
}
~BossA() {
for (auto it = mHero.begin(); it != mHero.end(); it++) {
delete (*it);
}
}
private:
list<AbstractHero*> mHero;
};
void test1() {
AbstractHero* heroA = new HeroA;
AbstractHero* heroB = new HeroB;
AbstractHero* heroC = new HeroC;
AbstractHero* heroD = new HeroD;
AbstractBoss* bossA = new BossA;
bossA->AddHero(heroA);
bossA->AddHero(heroB);
bossA->AddHero(heroC);
bossA->AddHero(heroD);
cout << "英雄A 阵亡!" << endl;
bossA->DelHero(heroA);
cout << "Boss死亡!" << endl;
bossA->Modify();
}
上述代码运行结果如下
英雄A正在打Boss!
英雄B正在打Boss!
英雄C正在打Boss!
英雄D正在打Boss!
英雄A 阵亡!
Boss死亡!
英雄B停止打Boss!
英雄C停止打Boss!
英雄D停止打Boss!
观察者模式的优缺点
优点
- 观察者模式可以实现表示层和数据逻辑层的分离,并定义了稳定的消息更新传递机制,抽象了更新接口,使得可以有各种各样不同的表示层作为具体观察者角色。
- 观察者模式在观察目标和观察者之间建立一个抽象的耦合。
- 观察者模式支持广播通信。
- 观察者模式符合“开闭原则”的要求。
缺点
- 如果一个观察目标对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
- 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
- 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。