C++观察者模式
当对象间存在一对多关系时,则使用观察者模式(observer pattern)。比如,当一个对象被修改时,则会自动通知发依赖它的对象。观察者模式属于行为型模式。
Observer模式是应用最多、影响最广的设计模式之一,因为Observer的一个实例Model/View/Control(MVC)结构在系统开发架构设计中有着很重要的地位和意义,MVC实现了业务逻辑和表示层的解耦。在MFC(微软基础类库)中,Doc/View(文档视图结构)提供了实现MVC的框架结构。
在GOF的《设计模式:可复用面向对象软件的基础》一书中对观察者模式是这样定义的:定义对象的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。当一个对象发生了变化,关注它的对象就会得到通知;这种交互也成为发布-订阅(publish-subscribe)。
MVC框架是模型-视图-控制器的缩写,一种软件设计典范。用一种业务逻辑、数据、显示界面分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能。在一个逻辑的图形化用户界面的结构中MVC分层有助于管理复杂的应用程序,因为可以在一个时间内专注的关注一个方面,可以在不依赖业务逻辑的情况下专注于视图设计,同时也将应用程序的测试更加容易。
介绍
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并被自动更新。
主要解决:一个对象状态改变并给其它对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
如何解决:使用面向对象技术,可以将这种依赖关系弱化。
关键代码:在抽象类里有一个arrayList存放观察者们。
应用实例:1、拍卖的时候,拍卖师观察最高价格,然后通知给其它竞价者竞价。2、西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水找来一个老乌龟,这个老乌龟就是观察者(菩萨是被观察者),他观察菩萨这个动作。
优点:1、观察者和被观察者是抽象耦合的。2、建立一套触发机制。
缺点:1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
使用场景:
- 一个抽象模型有两个方面,其中一个方面发依赖于另外一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
- 一个对象的改变将导致其它一个或多个对象发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
- 一个对象必须通知其他对象,而并不知道这些对象是谁。
- 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
注意事项:1、应避免循环引用。2、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式(多线程)。
示例1
对同一组数据进行统计分析的时候,我们希望能够提供多种形式的表示(例如以表格形式进行显示、柱状图统计显示、百分比统计显示等)。这些表示都依赖于同一组数据,我们当然需要当数据改变的时候,所有统计的显示都能同时改变、Observer模式就是解决了这一个问题。
UML类图
- Subject(目标)
- 目标知道它的观察者,可以有任意多个观察者观察用一个目标。
- 提供注册和删除观察者对象的接口。
- Observer(观察者)
- 为所有观察目标的观察者对象提供一个公共的更新接口。
- ConcreteSubject(具体目标)
- 将有关状态的情况广播给各ConcreteObserver对象。
- ConcreteObserver(具体观察者)
- 维护一个指向ConcreteSubject对象的指针。用于当具体观察者初始化时直接存入ConcreteSubject对象(初始化就订阅ConcreteSubject主题)。
- 存储有有关状态,这些状态应与目标的状态保持一致。
- 实现Observer公共更新接口以便使自身状态和目标状态保持一致。
观察者模式按照以下方式进行协作:
- 当ConcreteSubject发生任何可能导致其观察者与其本身状态不一致的改变时,它将通知它的各个观察者。
- 在得到一个具体目标的改变通知后,ConcreteObserver对象可收到目标对象的信息。ConcreteObserver使用这些信息使它的状态和目标对象的状态保持一致。
代码实现:
#include <iostream>
#include <list>
using std::cout;
using std::endl;
using std::cin;class Observer{public:virtual ~Observer() {};virtual void Update(int) = 0;};class Subject{public:virtual ~Subject() {};virtual void Attach(Observer*) = 0;virtual void Detach(Observer*) = 0;virtual void Notify() = 0;};class ConcreteObserver : public Observer{private:Subject *_pSubject;public:ConcreteObserver(Subject* pSubject) :_pSubject(pSubject){//在目标中注册当前观察者(此处的观察者是广义上的观察者,目标并不知道具体谁要观察它,目标只进行广播即可)this->_pSubject->Attach(this);cout << "I'm the observer \" 1 \".\n";}void Update(int value) override{cout << "ConcreteObserver get the update.New State:" << value << endl;}};class ConcreteObserver2 : public Observer{private:Subject *_pSubject;public:ConcreteObserver2(Subject* pSubject) :_pSubject(pSubject){//在目标中注册当前观察者(此处的观察者是广义上的观察者,目标并不知道具体谁要观察它,目标只进行广播即可)this->_pSubject->Attach(this);cout << "I'm the observer \" 2 \".\n";}void Update(int value) override{cout << "ConcreteObserver2 get the update.New State:" << value << endl;}};class ConcreteSubject :public Subject{private:std::list<Observer*> _pObserverList;int _iState;public:void SetState(int state){_iState = state;}void Attach(Observer* pObserver) override{_pObserverList.push_back(pObserver);}void Detach(Observer* pObserver) override{_pObserverList.remove(pObserver);}void Notify() override{auto begin = _pObserverList.begin();auto end = _pObserverList.end();while (begin != end){(*begin)->Update(_iState);begin++;}}};int main()
{//创建目标ConcreteSubject *pSubject = new ConcreteSubject();//创建观察者Observer *pObserver = new ConcreteObserver(pSubject);Observer *pObserver2 = new ConcreteObserver2(pSubject);//改变当前状态pSubject->SetState(2);//广播给所有广义上的观察者pSubject->Notify();//去除某个观察者pSubject->Detach(pObserver);//改变当前状态pSubject->SetState(3);//重新广播pSubject->Notify();//结束,释放对象delete pObserver;delete pObserver2;delete pSubject;return 0;
}
输出结果(vs2017):
示例2(高阶版)
#include <iostream>
#include <list>
#include <string>
using std::cout;
using std::endl;
using std::cin;/*
观察者模式有许多具有类似含有的不同术语,比如主题也成为发布者,观察者通常称为订阅者。
动词“观察”、“倾听”或“跟踪”通常表示相同的意思。
*/class IObserver
{
public:virtual ~IObserver() {}virtual void Update(const std::string &messageFromSubject) = 0;
};class ISubject
{
public:virtual ~ISubject() {}virtual void Attach(IObserver *observer) = 0;virtual void Detach(IObserver *observer) = 0;virtual void Notify() = 0;
};//具体目标拥有一些重要的状态,并在状态更改时通知观察者
class Subject : public ISubject
{
private:std::list<IObserver *> _listObserver;std::string _messgae;
public:virtual ~Subject(){cout << "Goodbye,I was the Subject.\n";}//定义订阅管理方法void Attach(IObserver* observer) override{_listObserver.push_back(observer);}void Detach(IObserver* observer) override{_listObserver.remove(observer);}void Notify() override{std::list<IObserver *>::iterator begin = _listObserver.begin();HowManyObserver();while (begin != _listObserver.end()){(*begin)->Update(_messgae);++begin;}}void CreateMessage(std::string message = "Empty"){this->_messgae = message;Notify();}void HowManyObserver(){cout << "There are " << _listObserver.size() << " observers in the list\n";}/*通常的来说,订阅逻辑知识主题真正能做的事情的一小部分。主题通常拥有一些重要的业务逻辑,每当重要的事情即将发生(或者发生之后)时触发通知方法。*/void SomeBusinessLogic(){this->_messgae = "change message message";Notify();cout << "I'm about to do some thing important.\n";}
};class Observer :public IObserver
{
private:std::string _messageFromSubject;Subject &_subject;static int _staticNumber;int _number;
public:Observer(Subject &subject) :_subject(subject){this->_subject.Attach(this);cout << "Hi,I'm the Observer \"" << ++Observer::_staticNumber << "\".\n";this->_number = Observer::_staticNumber;}~Observer(){cout << "Goodbye, I was the Observer \"" << this->_number << "\".\n";}void Update(const std::string &messageFromSubject) override{this->_messageFromSubject = messageFromSubject;PrintInfo();}void RemoveMeFromTheList(){this->_subject.Detach(this);cout << "Observer \"" << this->_number << "\" removed from the list.\n";}void PrintInfo(){cout << "Observer \"" << this->_number << "\": a new message is aviilable -->" << this->_messageFromSubject << "\n";}};int Observer::_staticNumber = 0;void ClientCode()
{Subject * subject = new Subject();Observer *observer1 = new Observer(*subject);Observer *observer2 = new Observer(*subject);Observer *observer3 = new Observer(*subject);Observer *observer4;Observer *observer5;subject->CreateMessage("hello world! :D");observer3->RemoveMeFromTheList();subject->CreateMessage("The weather is hot today! :p");observer4 = new Observer(*subject);observer2->RemoveMeFromTheList();observer5 = new Observer(*subject);observer4->RemoveMeFromTheList();observer1->RemoveMeFromTheList();delete observer5;delete observer4;delete observer3;delete observer2;delete observer1;delete subject;
}int main()
{ClientCode();return 0;
}
输出结果(vs2017):
参考文章
https://www.runoob.com/design-pattern/observer-pattern.html
https://www.cnblogs.com/carsonzhu/p/5770253.html