观察者模式
使用场景:
为了实现对两个系统的解耦,该模式是最有效和好用的模式。
当底层系统判断某些功能触发时,会像管线发送一条“消息”,通知他tigger触发,当上层
的某个系统注册为观察者时,它可以对该消息进行响应。即使移除了上层系统,“消息”仍然
会发送到管线,只是没人接收
优缺点:
1)被观察者维护的观察者列表,添加观察者时是动态分配内存的
解决方案:链式观察者
todo:
2)同步问题,事件队列或可解决?
3)观察者的销毁导致的空指针
解决方案:待补充
/*---------------------------------观察者模式-----------------------------
*/
// -------------------------------Demo-----------------------------//
// 观察者,消息的接收方
class Observer
{
private:
void A_execute()
{
// 执行事件A的内容
}
public:
Observer() {}
~Observer() {}
virtual void onNotify(const Enity &entity, Event event)
{
// 检测收到的通知类型
switch (event)
{
case Event_A:
A_execute();
break;
// and so on
}
}
};
// 被观察者,消息的发送方
class Subject
{
private:
// 一个列表,存储需要通知的观察者
Observer *_Observers[100];
public:
Subject(/* args */) {}
~Subject() {}
// 添加删除API
void addObserver(Observer *observer);
void removeObserver(Observer *observer);
protected:
/*
protected使得只有继承了Subject的派生类才能发送通知
改进:更加好的解决方案是,不用这个继承,对于需要被观察的对象,可以生成一个Subject实例
对象,用这个实例来通知观察者,但需要通知时,调用Subject.notify()就行
*/
// 发送通知,遍历观察者列表,对其中每一个观察者发送通知
void notify(const Enity &entity, Event event)
{
// 注意:此时不能在notify()方法里修改_Observers数组长度,否则会导致for循环Bug
for (auto &&i : _Observers)
i->onNotify(entity, event);
}
};
命令模式
使用场景:类似于观察者模式,实现对命令的接收者和发送者的解耦。
优缺点:待补充
/*---------------------------------命令模式-----------------------------
*/
// -------------------------------Demo-----------------------------//
// 基类命令类
class Command
{
public:
// 执行接口
virtual void execute() = 0;
};
// 具体命令(子命令)类
class ConcreteCommand : public Command
{
private:
/* 若要支持撤销,可以:
1、用几个变量记录execute()会改变的变量的先前状态,在undo()中恢复状态
2、持久化数据结构
*/
Receiver *_receiver;
public:
// 构造函数,实现了这个命令对象对接收者的绑定
ConcreteCommand(Receiver *receiver) : _receiver(receiver){};
virtual void execute() override
{
// 接收者执行其内部的execute()
_receiver->execute();
}
};
// 接收者类
class Receiver
{
public:
// 接收者内部的执行(这是真正执行的地方,比如移动,这里的execue()应该调用unity的Move()来移动)
void execute();
};
// 调用者(即创建命令的人)类
class Invoker
{
private:
// 命令池,调用者需要实现的一系列命令
Command *_commandPool[100];
public:
// 设置命令
void setCommand(Command *command, int index)
{
// 没有检测数组越界
_commandPool[index] = command;
}
// 顺序执行命令池中的命令
void invoke()
{
for (auto &&i : _commandPool)
i->execute();
};
};
// 客户端
int main(int argc, char const *argv[])
{
// 创建接收者
Receiver *receiver = new Receiver();
// 创建命令对象,设定它的接收者,由接收者实现具体命令行为
ConcreteCommand concreteCommand(receiver);
// 创建调用者,把命令对象设置进去
Invoker invoker;
invoker.setCommand(&concreteCommand, 0);
invoker.invoke();
return 0;
}
事件队列
如果只是想要解耦接收者和发送者,应该使用命令模式和观察者模式,事件队列是用来处理需要
“立即”执行的命令是采用的,因此事件队列应该是建立在命令模式和观察者模式上的扩展。
使用场景:代码A需要代码B去执行某些操作,A应该将请求(在命令模式中是“命令”,在观察者
模式中是“消息”)推给B。同时B应该在合适的时候将请求拉取下来执行。队列在二者中构建了
一个缓存,可以实现延迟、合并、忽视这些请求,因此请求的控制权从请求者A中转移到接收者B
中。(在命令模式中,请求方A可以控制什么时候发送、是否发送、发送几条请求,接收方B只负
责收到请求后执行;在观察者模式中类似,由被观察者控制请求,观察者只能执行请求)
注意:事件队列比较复杂,需要仔细考虑是否使用
代码以后有时间再补充
/*---------------------------------事件队列-----------------------------
*/
// -------------------------------Demo-----------------------------//
这本书的pdf放在这里:
链接:https://pan.baidu.com/s/1OFDVrZcZqKSAo77HfDvuYA?pwd=7xt3
提取码:7xt3
有需要的自取