Qt-命令模式
来源:互联网 发布:关键词优化在哪里 编辑:程序博客网 时间:2024/05/18 20:12
命令模式
定义:将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。
类图
客户角色(Client): 创建具体的命令对象,并且设置命令对象的接收者。
命令角色(Command): 定义命令的接口,声明执行的方法。这是一个抽象类。
具体命令角色(ConcreteCommand):命令接口实现对象,是“虚”的实现;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。
请求者角色(Invoker):要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。
接收者角色(Receiver):接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。
例子:
根据大多数例子就是 遥控器和饭店点餐,这里使用遥控器。
先看个 很丑的 遥控器图
根据介绍,命令模式是一个解耦的模式。
假设这个界面是遥控器,那么我们这些按钮执行的函数永远是一样的,不变的。
不会因为我想让第一个按钮打开 厨房灯,而却 更改 触发第一个按钮的内容。
请参照我上传的资源 (半夜上传没有审核啊,不知道百度网盘连接能用不:http://pan.baidu.com/s/1i5xzZKD)
来看 CommandPattern(1)
在这个遥控器例,我们的按钮槽函数永远不变:
void RemoteControl::on_pushButton_clicked(){ m_pInvoker->doOnCommand(0);}void RemoteControl::on_pushButton_2_clicked(){ m_pInvoker->doOffCommand(0);}void RemoteControl::on_pushButton_3_clicked(){ m_pInvoker->doOnCommand(1);}void RemoteControl::on_pushButton_4_clicked(){ m_pInvoker->doOffCommand(1);}......void RemoteControl::on_pushButton_15_clicked(){ m_pInvoker->doUndo();}
那么我们怎么通过触发第一个按钮就实现 客厅开灯这个操作?
我们用一个类Invoker 来管理操作命令, 这里有两组 一个是开,一个是关。
Invoker.h
public: NoCommand * m_pNoCommand; // 未定义命令 QVector<ICommand *> onCommands; // "开" 命令组 QVector<ICommand *> offCommands; // "关" 命令组public: // 设置遥控器 按键 对应的 开命令 关命令 void setCommand(int slot, ICommand *onCommand, ICommand *offCommand); void doOnCommand(int slot); // "开" 命令 的执行 void doOffCommand(int slot); // "关" 命令 的执行Invoker.cpp 具体实现:
Invoker::Invoker(){ m_pNoCommand = new NoCommand; // 初始化 开 关 两组命令 for(int i = 0; i < SLOTNUMBER; i++) { onCommands.push_back(m_pNoCommand); offCommands.push_back(m_pNoCommand); }}// 设置按钮对应命令 开 关void Invoker::setCommand(int slot, ICommand *onCommand, ICommand *offCommand){ onCommands[slot] = onCommand; offCommands[slot] = offCommand;}// 执行对应slot(按键)的 开命令void Invoker::doOnCommand(int slot){ onCommands[slot]->Excute();}// 执行对应slot(按键)的 关命令void Invoker::doOffCommand(int slot){ offCommands[slot]->Excute();}
根据前面类图,我们知道有一个 Command的接口,供所有Command实现具体命令,比如开灯,关灯,开风扇.....
命令接口
ICommand.h
class ICommand{public: virtual ~ICommand() {} virtual void Excute() = 0;};
各个接口子类,实现纯虚函数内容
首先定义 一个灯 这个物体
.h
class Light : public QObject{ Q_OBJECTpublic: explicit Light(QString name, QObject *parent = 0); void printName();signals:public slots:private: QString strName;};
,cpp
#include "Light.h"#include <QDebug>Light::Light(QString name, QObject *parent) : QObject(parent), strName(name){}void Light::printName(){ qDebug() << strName;}
关于灯 有两个操作命令
开 与 关
LightCommand.h
#include "ICommand.h"#include "Light.h"class LightOnCommand : public ICommand{public: LightOnCommand(Light * m_pLight);public: Light * m_pLight; void Excute(); void Undo();};class LightOffCommand : public ICommand{public: LightOffCommand(Light * m_pLight);public: Light * m_pLight; void Excute(); void Undo();};LightCommand.cpp
#include "LightCommand.h"#include <QDebug>LightOnCommand::LightOnCommand(Light * light){ this->m_pLight = light;}void LightOnCommand::Excute(){ m_pLight->printName(); qDebug() << "开灯";}void LightOnCommand::Undo(){ m_pLight->printName(); qDebug() << "关灯";}LightOffCommand::LightOffCommand(Light * light){ this->m_pLight = light;}void LightOffCommand::Excute(){ m_pLight->printName(); qDebug() << "关灯";}void LightOffCommand::Undo(){ m_pLight->printName(); qDebug() << "开灯";}
再来看下,我需要的东西够了没?
我们需要一个遥控器,点击按钮 实现对应的命令(点击开,灯亮;关,灯灭),简单的来看,我们所需要的东西齐了。
现在我们定义 一个 客厅的灯 和 一个餐厅的灯
m_pLivingRoomLight = new Light("living Room"); m_pKitchenLight = new Light("Kitchen");
对应应该有四个命令 2个开,2个关
m_pLivingRoomLightOn = new LightOnCommand(m_pLivingRoomLight); m_pLivingRoomLightOff = new LightOffCommand(m_pLivingRoomLight); m_pKitchenLightOn = new LightOnCommand(m_pKitchenLight); m_pKitchenLightOff = new LightOffCommand(m_pKitchenLight);
同时定义 遥控器 第一组 按钮 操控 客厅的灯
第二组 操作 厨房的灯
m_pInvoker->setCommand(0, m_pLivingRoomLightOn, m_pLivingRoomLightOff); m_pInvoker->setCommand(1, m_pKitchenLightOn, m_pKitchenLightOff);
应该所要做的都完成了。
运行点击:
"living Room"
开灯
"living Room"
关灯
"Kitchen"
开灯
"Kitchen"
关灯
No CommandNo Command
点击前两组 ,我们会对应实现,相应的命令操作,后面几组按钮都是显示 No Command
这里使用No Command初始化,是为了,不用每次执行命令操作都判断命令存在不。 if(onCommands[slot] != NULL)
点击其他按钮,只要我们不操作就好了。
-----------------------------------------------分割线-------------------------------------------------------
根据定义 命令模式也支持可撤销的操作
此部分请参照:CommandPattern(2)
所以我们需要加一个撤销操作,撤销也是对命令的操作,所以只用在命令部分添加相应的“返回”操作。
ICommand.h
virtual void Undo() = 0;
对应继承的具体命令去实现这个虚函数即可。
对于灯来说 开的前一步是关, 关的前一步是开。 简单理解 就是我点击撤销,然后灯的操作取反即可。
void LightOnCommand::Undo(){ m_pLight->printName(); qDebug() << "关灯";}
....
其他类 类似这么实现。
现在运行看是否可以撤销(感觉这么撤销很假,简单先这么理解)
//点击开灯
"living Room"
开灯
//点击关灯
"living Room"
关灯
//啊,点击错了,我其实不想关灯,撤销下
"living Room"
开灯
//点击开灯
"Kitchen"
开灯
//点击关灯
"Kitchen"
关灯
//啊,点击错了,我其实不想关灯,撤销下
"Kitchen"
开灯
ps: 在header first里面撤销只能操作一次,我这里用的数组,可以连续撤销。
但是好像有问题,有空再看。而且假设连续点击开灯操作是不合理的,为了了解模式,
我们这里不做非法操作(*^__^*) ……
-----------------------------------------------分割线--------------------------------
此部分看CommandPattern(3)
例如风扇有 低速 中速 高速 关闭 这几个操作
按照书上说的
低速-关闭
中速-关闭
高速-关闭
这样三组命令操作就可以了。如果这样类似 前面 开灯关灯定义即可
这里在执行操作里面判断速度(按照 低->中->高 的速度 执行命令操作)
具体看下代码,不做叙述,没有多大意思。。
-----------------------------------------------分割线--------------------------------
主要看下 最后一部分 CommandPattern(4)
宏定义命令。
我定义了第七组按钮 开操作: 当点击 第七组的开, 客厅与厨房的灯 ,还有客厅的风扇都打开
点击第七组关按钮, 客厅、厨房的灯,客厅的风扇都关闭。
在遥控器里面,定义两组 命令 数组
QVector<ICommand*> partyOn; QVector<ICommand*> partyOff;
然后定义宏命令,
MacroCommand * m_pOnCommands; MacroCommand * m_pOffCommands;
我们把所有的开命令, 和关命令放到 他们的数组里面
partyOn.push_back(m_pLivingRoomLightOn); partyOn.push_back(m_pKitchenLightOn); partyOn.push_back(m_pCeilingFanOnCommand); partyOff.push_back(m_pLivingRoomLightOff); partyOff.push_back(m_pKitchenLightOff); partyOff.push_back(m_pCeilingFanOffCommand);
并且放到宏命令里面,执行 操作 和 撤销连个操作(当然是循环操作)
m_pOnCommands = new MacroCommand(partyOn); m_pOffCommands = new MacroCommand(partyOff);
设置第七组按钮 的触犯内容
m_pInvoker->setCommand(6, m_pOnCommands, m_pOffCommands);
宏命令可以这么理解,就是操作一堆命令,利用循环操作就行了,和普通命令没有没什么区别。
看下MacroCommand.h
#include <QVector>#include "ICommand.h"class MacroCommand : public ICommand{public: explicit MacroCommand(QVector<ICommand*>); QVector<ICommand*> m_pCommands;public: void Excute(); void Undo();};
除了命令变成数组外,没什么别的区别。
执行命令 循环即可
for(int i = 0; i < m_pCommands.count(); i++) { m_pCommands[i]->Undo(); }
写到这里基本完了。
所有内容按照header first 设计模式写的, 如果真的遇到一个要命令模式情景去写代码,还是不会用 O(∩_∩)O哈哈~
最后说下要点:
1. 命令模式将发出请求的对象和执行请求的对象解耦。
2. 在被解耦的两者之间是通过命令对象进行沟通的。命令对象封装了接受者和一个或者一组动作。
3. 调用者通过调用命令对象Excute 发出请求,这会使得接受者的动作被调用。
4. 调用者可以接受命令当作参数,甚至运行时动态的进行。
5. 命令可以支持撤销,做法是实现一个undo()方法来回到Excute()被执行之前的状态。
6. 宏命令是命令的一种简单的延伸,允许调用多个命令。宏方法也可以支持撤销。
7. 实际操作时,很常见使用“聪明”命令对象,也就是直接实现了请求,而不是将工作委托给接受者。
8. 命令也可以用来实现日志和事物系统。
- Qt-命令模式
- 关于回调函数、信号槽(Qt)、命令模式
- linux下使用命令模式去编译Qt程序
- QT命令
- QT学习之二:Qt命令模式下建立编译的第一个例子
- qt执行cmd命令
- Qt 命令 -qws
- VS2010编译Qt命令
- qt程序打包命令
- qt 运行cmd命令
- QT 配置命令
- qt执行cmd命令
- Qt 执行CMD命令
- Qt: windeployqt命令使用
- Qt运行cmd命令
- 命令模式
- 命令模式
- 命令模式
- 进制转换
- fzu2150——Fire Game(BFS)
- [iOS] Obnjective-C 中的 __block 是什么意思
- 打开相机和获取相册图片后实现裁剪
- Android第二周(第三部分)-GridView和ScrollView
- Qt-命令模式
- 逆序输出单个数字
- 我相信,终有一天,我会变成自己喜欢的模样
- [LightOJ 1274] Beating the Dataset (期望DP)
- listagg小记录
- win32 Socket网络编程连接
- Web安全渗透
- Android加载动画系列——WifiLoadingAnim
- 指针强制转换