这是个人学习编程模式的系列学习笔记第十一篇。
采用Qt Creator进行编写,但尽量采用C++基础语法。
观察者模式(Observer Pattern):定义对象间的一种一对多的依赖关系 ,当一个对象的状态发生改变时 , 所有依赖于它的对象
都得到通知并被自动更新。观察者模式又叫:依赖(Dependents)或发布-订阅模式(Publish-Subscribe)。
个人觉得发布-订阅模式最容易理解这个模式的含义。
一个对象作为发布者,多个对象作为订阅者,在发布者有状态变化的时候,会通知到全部的订阅者。最容易理解的是,一个人写了博客,有多个订阅者,如果博客更新,会通知全部的订阅者。
观察者模式主要用于:有两类对象关系密切,但有不希望两类对象紧耦合,通过抽象对象之间的接口实现发布对象状态的变化能通知到全部订阅对象。
观察者模式可以采用推模式和拉模式进行通知广播。推模式提供给观察者全部所需的信息,拉模式提供给观察者最小需要的信息,由订阅者向发布者询问更多需要的细节。
后期发展的委托技术是观察者模式的进一步升级。能实现对不同接口的通知(接口的参数需要相同)。
场景描述
假设要设计一个对外接收指令的接口,各种指令对应不同的类来处理,可能是多个,也可能是1个。接收到这个指令的类根据自己的情况作出对应的指令响应。
这有点像一个URL分发器,将前台接收到的请求发送给后面的多个业务处理器,业务处理器根据URL内容决定如何响应。
设计思路
定义一个发布接口类,包含订阅者附加、去除,订阅通知接口,派生具体的发布者类,接收指令并完成通知接口的调用。
定义一个订阅接口类,包括通知消息处理接口。派生多个具体的订阅者对指令进行各自的处理。
采用观察者模式的推模式(在进行通知广播的时候,将观察者需要的全部信息都提供给观察者,本示例需要的就是一个命令字符串,采用推模式感觉更解耦)。
UML
代码
#include <iostream>
#include <algorithm>
#include <list>
using namespace std;
class Processor
{
public:
virtual void execute(string cmd) = 0;
};
class Distribute
{
protected:
bool m_status = false;
list <Processor*> m_processors;
public:
virtual void attach(Processor* process) {m_processors.push_back(process);}
virtual void detach(Processor* process) {m_processors.remove(process);}
virtual string getCmd() = 0;
virtual void notify()
{
if(m_status)
{
list<Processor*>::iterator iter = m_processors.begin();
for(; iter != m_processors.end(); iter++)
{
(*iter)->execute(getCmd());
}
m_status = false;
}
}
};
class ConcreteDistribute : public Distribute
{
private:
string m_cmd;
public:
string getCmd()
{
if(m_status == false)
{
cout<<"Please input a cmd: \n";
getline(cin, m_cmd);
transform(m_cmd.begin(), m_cmd.end(), m_cmd.begin(), ::toupper);
m_status = true;
}
return m_cmd;
}
};
class DirProcessor : public Processor
{
public:
void execute(string cmd)
{
if(cmd == "DIR") cout<<"DirProcessor say: This is a DIR cmd!\n";
else cout<<"DirProcessor say: I don't know this cmd!\n";
}
};
class DelProcessor : public Processor
{
public:
void execute(string cmd)
{
if(cmd == "DEL") cout<<"DelProcess say: This is a DEL cmd!\n";
else cout<<"DelProcessor say: I don't know this cmd!\n";
}
};
int main()
{
ConcreteDistribute* distributer = new ConcreteDistribute();
DirProcessor* dirProcessor = new DirProcessor();
DelProcessor* delProcessor = new DelProcessor();
distributer->attach(dirProcessor);
distributer->attach(delProcessor);
distributer->getCmd();
distributer->notify();
distributer->detach(delProcessor);
distributer->detach(dirProcessor);
delete delProcessor;
delete dirProcessor;
delete distributer;
return 0;
}