1、观察者模式又叫做发布-订阅(Publish/Subscribe)模式。
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一主题对象,这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
UML图如下:
2、观察者模式的特点:
将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维护一致性而使各类紧密耦合,这样会给维护、扩展、和重用都带来不便。
而观察者模式的关键对象是主题Subject和观察者Observer,一个Subject可以有任意数目的依赖它的Observer,一旦Subject的状态改变,所有的Observer都可以得到通知。Subject发出通知时并不需要知道谁是它的观察者,也就是说,具体观察者是谁,它根本不需要知道。而任何一个具体的观察者不知道,也不需要知道其他观察者的存在。
3、什么时候需要考虑使用观察者模式呢?
答:当一个对象的改变需要同时改变其他对象的时候。而且它不知道具体有多少个对象有待改变时, 应该考虑使用观察者模式,当一个抽象模型有两个方面,其中一方面依赖于另一方面,这时用观察者模式可以将这两都封装在独立的对象中使它们各自独立地改变和复用。
总的来说,观察者模式所做的工作其实就是在解除耦合。让耦合的双发都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。
4、观察者模式的不足:
尽管已经用了依赖倒转原则,但是“抽象通知者”还是依赖“抽象观察者”,也就是说,万一没有了抽象观察者这样的接口,通知的功能就完不成了。另外就是每个具体观察者,它不都要调用“更新”方法。
5、C++代码实现
- #include <iostream>
- #include <vector>
- #include <string>
- #include <algorithm>
- using namespace std;
- /* Observer类,抽象观察者,为所有的具体观察者定义
- 了一个接口,在得到主题的通知时更新自己。这个接口
- 叫做更新接口。抽象观察者一般用一个抽象类或者一个
- 接口实现。更新接口通常包含一个Update()方法,这个
- 方法叫做更新方法。 */
- class Observer
- {
- public:
- virtual void Update() = 0;
- };
- /* Subject类,可翻译为主题或抽象通知者一般用抽象类或
- 抽象接口实现。它把所有对观察者对象的引用保存在一个
- 聚集里,每个主题都可以有任何数量的观察者。抽象主题
- 提供一个接口,可以增加和删除观察者对象。 */
- class Subject
- {
- private:
- //观察者对象的引用的聚集容器
- vector<Observer *> observers;
- public:
- //添加主题的观察者
- void Attach(Observer * p_observer) {
- observers.push_back(p_observer);
- }
- //删除主题的观察者
- void Detach(Observer * p_observer) {
- vector<Observer *>::iterator it;
- it = find(observers.begin(), observers.end(), p_observer);
- observers.erase(it);
- }
- //通知所有和主题关联的观察者,主题对象的状态改变了
- void Notify() {
- int length = observers.size();
- for (int i = 0; i < length; ++i) {
- observers[i]->Update();
- }
- }
- };
- /* ConcreteSubject类,具体主题或具体通知者,将有关
- 状态存入具体的观察者对象,在具体主题的内部状态发
- 生改变时,给所有登记过的观察者发出通知。具体主题
- 角色通常用一个具体子类实现 */
- class ConcreteSubject : public Subject
- {
- private:
- string subjectState;
- public:
- string GetSubjectState() {
- return subjectState;
- }
- void SetSubjectState(string newState) {
- subjectState = newState;
- }
- };
- /* ConcreteObserver类,具体观察者,实现抽象观察者角色
- 所要求的更新接口,一便使本身的状态与主题的状态相协调
- 。具体观察者角色可以保存一个指向具体主题对象的引用。
- 具体观察者角色通常用一个具体子类实现。 */
- class ConcreteObserver : public Observer
- {
- private:
- ConcreteSubject * p_subject;
- string name;
- string observerState;
- public:
- ConcreteObserver(ConcreteSubject * subject, string name) {
- ConcreteObserver::p_subject = subject;
- ConcreteObserver::name = name;
- }
- void Update() {
- observerState = p_subject->GetSubjectState();
- cout << "观察者/"" << name << "/"发现主题对象的新状态是/"" << observerState << "/""<<endl;
- }
- };
- int main()
- {
- ConcreteSubject s;
- s.SetSubjectState("上课了");
- ConcreteObserver * pa = new ConcreteObserver(&s, "X");
- ConcreteObserver * pb = new ConcreteObserver(&s, "Y");
- ConcreteObserver * pc = new ConcreteObserver(&s, "Z");
- s.Attach(pa);
- s.Attach(pb);
- s.Attach(pc);
- s.Notify();
- cout << endl;
- s.Detach(pa);
- s.SetSubjectState("上课中");
- s.Notify();
- cout << endl;
- s.Detach(pc);
- s.SetSubjectState("下课了");
- s.Notify();
- return 0;
- }