zl程序教程

您现在的位置是:首页 >  后端

当前栏目

C++命令模式

2023-09-11 14:19:13 时间

在应用程序中,经常需要对相同的数据执行多个操作。可以这样简单的理解:用户有一些数据,并且界面提供了很多选项,他可以对该数据进行其中的某些操作。例如,图片处理器:可以选择旋转/翻转/反转照片。另外,一旦有不满意的处理,很有可能需要撤消。这就是基本的业务逻辑,必须要在实现过程中考虑清楚。

大多情况下,最容易想到的是:当动作触发后,调用接收者对应的方法。但是这存在不少问题:


要写很多条件代码,必须为每个操作编写非常类似的代码。 如果有更多的命令,就会发现需要经常更改现有代码。

显然,这种无法抵御变化的紧耦合设计不太合适。在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,实现二者之间的松耦合。这就是命令模式。


Command:定义命令的接口,声明执行的方法。 ConcreteCommand:命令接口实现对象,是“虚”的实现;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。 Receiver:接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。 Invoker:要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。 Client:创建具体命令对象,并设置其接收者(注意: 这并非常规意义上的客户端,而是在组装命令对象和接收者。或许,把这个 Client 称为装配者会更好理解,因为真正使用命令的客户端是从 Invoker 来触发执行)。

简易计算器主要专注于理解命令模式,而不是实现一个真正的计算器。因此,将对一组固定的数据(x = 20, y = 10)进行基本操作(加减乘除)。假设将创建一个这样的应用程序:

这里写图片描述

从 Command 类开始,这是一个抽象类,将被用作执行命令的接口。其他的 ConcreteCommand 类派生自它,提供了具体的命令(加减乘除)。

command.h


准备好命令的层次结构后,现在来编写真正的程序逻辑 - 需要一个类来进行计算 - Reciever,提供了一个接口表示接收者的主要功能,其中 Calculator 类为 Reciever 的具体实现:

reciever.h


virtual void setAction(TYPES::ACTION action) = 0; // 设置具体命令 virtual int getResult() = 0; // 获取命令结果 // 命令接收者具体实现 class Calculator : public IReciever public: Calculator(int x, int y); void setAction(TYPES::ACTION action); int getResult(); private: int m_nX; int m_nY; TYPES::ACTION m_currentAction; #endif // RECIEVER_H

reciever.cpp


需要一个类,让用户选择要执行的命令 - Client(也就是 UI)。最后,还需要写一个 Invoker,在示例中依然是用户界面。

widget.h


QButtonGroup *pButtonGroup = new QButtonGroup(this); QRadioButton *pAddButton = new QRadioButton("+", this); QRadioButton *pSubButton = new QRadioButton("-", this); QRadioButton *pMulButton = new QRadioButton("*", this); QRadioButton *pDivButton = new QRadioButton("/", this); QLabel *pLabel = new QLabel("x = 20, y = 10", this); m_pResultEdit = new QLineEdit(this); QPushButton *pCalButton = new QPushButton("Calculate", this); m_pResultEdit- setReadOnly(true); pButtonGroup- addButton(pAddButton, 0); pButtonGroup- addButton(pSubButton, 1); pButtonGroup- addButton(pMulButton, 2); pButtonGroup- addButton(pDivButton, 3); pButtonGroup- setExclusive(true); // 互斥 QHBoxLayout *pHLayout = new QHBoxLayout(); pHLayout- addWidget(pAddButton); pHLayout- addWidget(pSubButton); pHLayout- addWidget(pMulButton); pHLayout- addWidget(pDivButton); pHLayout- setContentsMargins(0, 0, 0, 0); pHLayout- setSpacing(10); QVBoxLayout *pLayout = new QVBoxLayout(this); pLayout- addWidget(pLabel); pLayout- addWidget(m_pResultEdit); pLayout- addLayout(pHLayout); pLayout- addWidget(pCalButton); pLayout- setSpacing(10); pLayout- setContentsMargins(10, 10, 10, 10); // 连接信号槽 connect(pButtonGroup, static_cast void (QButtonGroup::*)(int) ( QButtonGroup::buttonClicked), this, Widget::onButtonClicked); connect(pCalButton, QPushButton::clicked, this, Widget::onCalculate); // 默认选择加法 pButtonGroup- button(0)- click(); Widget::~Widget() // 设置当前命令 void Widget::onButtonClicked(int id) if (id == 0) { command = addCmd; } else if (id == 1) { command = subCmd; } else if (id == 2) { command = mulCmd; } else if (id == 3) { command = divCmd; // 计算结果 void Widget::onCalculate() int nResult = command- execute(); m_pResultEdit- setText(QString::number(nResult)); }

单选按钮所触发的事件只是改变要使用的命令对象,并且当用户选择计算时,将使用适当的命令对象来获取结果。


以打车为例,又是滴滴(~O(∩_∩)O~),用户发起一个“打车”命令,司机接单,到达终点时,用户再次发起一个“付款”命令,司机收款。

和上述示例类似, Command 是一个抽象类,将被用作执行命令的接口。其他的 ConcreteCommand 类派生自它,提供了具体的命令(打车/付款)。

command.h


开始,我们创建了两个命令(打车 付款),通过 Invoker 的 addCmd() 将他们加入至命令队列,然后 notify() 通知执行命令,这样,就形成了一个命令链。


要降低发送者和接收者之间的耦合度,命令模式是一种非常好的方式。在实现命令模式时,要记住,最重要的事情是:命令只是发送者和接收者之间的链接。它应该只告诉接收者发送者期望的是什么,而不应该以任何方式改变发送者和接收者的逻辑。


我学会了,命令模式 命令模式属于行为型模式,这个类型的设计模式总结出了 类、对象之间的经典交互方式,将类、对象的行为和使用解耦了,花式的去使用对象的行为来完成特定场景下的功能。
这就是命令,命令模式 命令模式:请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
浅谈JAVA设计模式之——命令模式(Command) 将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。