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 Command

No 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. 命令也可以用来实现日志和事物系统。

1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 门诊处方笺丢了怎么办 孕中期睡觉手麻怎么办 怀孕2个月了没胎心胎芽怎么办 怀孕腿疼的厉害怎么办 孕妇老是失眠多梦怎么办 孕妇会失眠多梦怎么办 怀孕5个月睡不着怎么办 6个月孕妇失眠怎么办 彩超脉络丛囊肿怎么办 双侧脉络丛囊肿怎么办 唐筛神经管缺陷高风险怎么办 雌激素低怎么办吃什么东西补 我怀了狗的孩子怎么办 结婚2年不要孩子怎么办 备孕一直没怀孕怎么办 刚生的婴儿打嗝怎么办 小孩40天黄疸高怎么办 婴儿身高长得慢怎么办 四个月的宝宝哭怎么办 孕39周羊水偏多怎么办 孕39周羊水浑浊怎么办 孕晚期羊水过少怎么办 怀孕脐带绕颈一周怎么办 nt检查宝宝趴着怎么办 四维胎儿有问题怎么办 怀孕70天没有胎心怎么办 怀孕20天不想要怎么办 换轮胎胎压监测怎么办 怀孕了吐的厉害该怎么办 怀孕吐完嗓子疼怎么办 怀孕16周不想要怎么办 怀孕四个月胎盘低置怎么办 孕37周胎盘三级怎么办 孕37周胎盘老化怎么办 怀孕22周胎盘1级怎么办 婴儿吃奶粉吐奶怎么办 宝宝吐奶又拉稀怎么办 羊水穿刺21三体怎么办 开始长妊娠纹了怎么办 已经长了妊娠纹怎么办 孕晚期长妊娠纹怎么办