cpp: Command Pattern
Posted ®Geovin Du Dream Park™
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了cpp: Command Pattern相关的知识,希望对你有一定的参考价值。
/*****************************************************************//** * \\file Gold.h * \\brief Command Pattern 命令模式 亦称:动作、事务、Action、Transaction、Command C++ 14 * 涂聚文 Geovin Du Visual Studio 2022 edit. * \\author geovindu * \\date 27 May 2023 *********************************************************************/ #pragma once #ifndef GOLD_H #define GOLD_H #include <iostream> #include <sstream> #include <vector> using namespace std; /** * @brief 类库空间名 * geovindu edit * */ namespace DuCommonPattern /// <summary> /// 类 /// </summary> class Gold public: /// <summary> /// 制作金项链 /// </summary> void GoldNecklace() cout << "制作一条金项链" << endl; /// <summary> /// 制作黄金耳环 /// </summary> void GoldEarring() cout << "制作一对黄金耳环" << endl; //做其他各种黄金鉓品......略 ; #endif /*****************************************************************//** * \\file Command.h * \\brief Command Pattern 命令模式 亦称:动作、事务、Action、Transaction、Command C++ 14 * 涂聚文 Geovin Du Visual Studio 2022 edit. * \\author geovindu * \\date 27 May 2023 *********************************************************************/ #pragma once #ifndef COMMAND_H #define COMMAND_H #include <iostream> #include <sstream> #include <vector> #include <list> #include "Gold.h" using namespace std; /** * @brief 类库空间名 * geovindu edit * */ namespace DuCommonPattern /// <summary> /// 制作各种黄金饰品对应的抽象类 /// </summary> class Command public: /// <summary> /// 构造函数 /// </summary> /// <param name="pgold"></param> Command(Gold* pgold) mPGold = pgold; /// <summary> /// 做父类时析构函数应该为虚函数 /// </summary> virtual ~Command() if (mPGold != nullptr) //delete mPGold; mPGold = nullptr; /// <summary> /// 执行 /// </summary> virtual void Execute() = 0; protected: /// <summary> /// 子类需要访问 /// </summary> Gold* mPGold; // ; #endif /*****************************************************************//** * \\file CommandNecklace.h * \\brief Command Pattern 命令模式 亦称:动作、事务、Action、Transaction、Command C++ 14 * 涂聚文 Geovin Du Visual Studio 2022 edit. * \\author geovindu * \\date 27 May 2023 *********************************************************************/ #pragma once #ifndef COMMANDNECKLACE_H #define COMMANDNECKLACE_H #include <iostream> #include <sstream> #include <vector> #include "Gold.h" #include "Command.h" using namespace std; /** * @brief 类库空间名 * geovindu edit * */ namespace DuCommonPattern /// <summary> /// 制作黄金项链命令 /// </summary> class CommandNecklace:public Command public: /// <summary> /// 构造函数 /// </summary> /// <param name="pgold"></param> CommandNecklace(Gold* pgold) :Command(pgold) /// <summary> /// 执行 /// </summary> virtual void Execute() mPGold->GoldNecklace(); ; #endif /*****************************************************************//** * \\file CommandEarring.h * \\brief Command Pattern 命令模式 亦称:动作、事务、Action、Transaction、Command C++ 14 * 涂聚文 Geovin Du Visual Studio 2022 edit. * \\author geovindu * \\date 27 May 2023 *********************************************************************/ #pragma once #ifndef COMMANDEARRING_H #define COMMANDEARRING_H #include <iostream> #include <sstream> #include <vector> #include "Gold.h" #include "Command.h" using namespace std; /** * @brief 类库空间名 * geovindu edit * */ namespace DuCommonPattern /// <summary> /// 制作黄金耳环命令 /// </summary> class CommandEarring:public Command public: /// <summary> /// 构造函数 /// </summary> /// <param name="pgold"></param> CommandEarring(Gold* pgold) :Command(pgold) /// <summary> /// 执行 /// </summary> virtual void Execute() mPGold->GoldEarring(); ; #endif /*****************************************************************//** * \\file Craftsman.h * \\brief Command Pattern 命令模式 亦称:动作、事务、Action、Transaction、Command C++ 14 * 涂聚文 Geovin Du Visual Studio 2022 edit. * * \\author geovindu * \\date May 2023 *********************************************************************/ #pragma once #ifndef CRAFTSMAN_H #define CRAFTSMAN_H #include <iostream> #include <sstream> #include <vector> #include <list> #include "Gold.h" #include "Command.h" using namespace std; /** * @brief 类库空间名 * geovindu edit * */ namespace DuCommonPattern /// <summary> /// 工匠 /// </summary> class Craftsman public: /// <summary> /// 添加 /// </summary> /// <param name="pcommand"></param> void AddCommand(Command* pcommand) mCommlist.push_back(pcommand); /// <summary> /// 删除 /// </summary> /// <param name="pcommand"></param> void DelCommand(Command* pcommand) mCommlist.remove(pcommand); /// <summary> /// 收到多个制作BOM单,按顺序制作 /// </summary> void Notify() for (auto iter = mCommlist.begin(); iter != mCommlist.end(); ++iter) (*iter)->Execute(); private: /// <summary> /// 多个制作BOM单列表 /// </summary> std::list<Command*> mCommlist; ; #endif /*****************************************************************//** * \\file TraineeCraftsman.h * \\brief Command Pattern 命令模式 亦称:动作、事务、Action、Transaction、Command C++ 14 * 涂聚文 Geovin Du Visual Studio 2022 edit. * \\author geovindu * \\date May 2023 *********************************************************************/ #pragma once #ifndef CRAFTSMAN_H #define CRAFTSMAN_H #include <iostream> #include <sstream> #include <vector> #include <list> #include "Gold.h" #include "Command.h" using namespace std; /** * @brief 类库空间名 * geovindu edit * */ namespace DuCommonPattern /// <summary> /// 实习工匠 /// </summary> class TraineeCraftsman public: /// <summary> /// 构造函数 /// </summary> /// <param name="pCommand"></param> TraineeCraftsman(Command* pCommand) :mPCommand(pCommand) /// <summary> /// 实习工匠交给工匠开始制作首饰 /// </summary> void Notify() mPCommand->Execute(); /// <summary> /// 析构函数 /// </summary> ~TraineeCraftsman() // if (mPCommand != nullptr) delete mPCommand; mPCommand = nullptr; private: /// <summary> /// 实习工匠,有BOM清单列表 /// </summary> Command* mPCommand; ; #endif /*****************************************************************//** * \\file CraftsmanOld.h * \\brief Command Pattern 命令模式 亦称:动作、事务、Action、Transaction、Command C++ 14 * 涂聚文 Geovin Du Visual Studio 2022 edit. * \\author geovindu * \\date May 2023 *********************************************************************/ #pragma once #ifndef CRAFTSMANOLD_H #define CRAFTSMANOLD_H #include <iostream> #include <sstream> #include <vector> #include <list> #include "Gold.h" #include "Command.h" using namespace std; /** * @brief 类库空间名 * geovindu edit * */ namespace DuCommonPattern /// <summary> /// /// </summary> class CraftsmanOld public: /// <summary> /// /// </summary> /// <param name="pcommand"></param> void SetCommand(Command* pcommand) mPCommand = pcommand; /// <summary> /// /// </summary> void Notify() mPCommand->Execute(); private: /// <summary> /// /// </summary> Command* mPCommand; ; #endif /*****************************************************************//** * \\file GeovinDu.h * \\brief Command Pattern 命令模式 亦称:动作、事务、Action、Transaction、Command C++ 14 * 涂聚文 Geovin Du Visual Studio 2022 edit. * \\author geovindu * \\date May 2023 *********************************************************************/ #pragma once #ifndef GEOVINDU_H #define GEOVINDU_H #include <iostream> #include <sstream> #include <vector> #include <list> #include "Gold.h" #include "Command.h" #include "CommandEarring.h" #include "CommandMacro.h" #include "CommandNecklace.h" #include "Craftsman.h" #include "TraineeCraftsman.h" #include "CraftsmanOld.h" using namespace std; /** * @brief 类库空间名 * geovindu edit * */ namespace DuCommonPattern /// <summary> /// /// </summary> class GeovinDu private: public: /// <summary> /// 显示实例 /// </summary> void displaySimple(); ; #endif /*****************************************************************//** * \\file GeovinDu.cpp * \\brief Command Pattern 命令模式 亦称:动作、事务、Action、Transaction、Command C++ 14 * 涂聚文 Geovin Du Visual Studio 2022 edit. * \\author geovindu * \\date May 2023 *********************************************************************/ #include "GeovinDu.h" using namespace std; /** * @brief 类库空间名 * geovindu edit * */ namespace DuCommonPattern /// <summary> /// 实例 /// </summary> void GeovinDu::displaySimple() DuCommonPattern::Gold* pgold = new DuCommonPattern::Gold(); pgold->GoldNecklace(); pgold->GoldEarring(); cout << "****" << endl; DuCommonPattern::Gold gold1; DuCommonPattern::Command* pcmd1 = new DuCommonPattern::CommandNecklace(&gold1); pcmd1->Execute(); DuCommonPattern::Command* pcmd2 = new DuCommonPattern::CommandEarring(&gold1); pcmd2->Execute(); cout << "****" << endl; DuCommonPattern::Gold gold2; DuCommonPattern::CraftsmanOld* pcraftsman = new DuCommonPattern::CraftsmanOld(); DuCommonPattern::Command* pcmd4 = new DuCommonPattern::CommandNecklace(&gold2); pcraftsman->SetCommand(pcmd4); pcraftsman->Notify(); DuCommonPattern::Command* pcmd5 = new DuCommonPattern::CommandEarring(&gold2); pcraftsman->SetCommand(pcmd5); pcraftsman->Notify(); cout << "****" << endl; DuCommonPattern::Gold gold3; DuCommonPattern::Command* pcmd3 = new DuCommonPattern::CommandNecklace(&gold3); DuCommonPattern::Command* pcmd6 = new DuCommonPattern::CommandEarring(&gold3); DuCommonPattern::Craftsman* pcraftsman2 = new DuCommonPattern::Craftsman(); pcraftsman2->AddCommand(pcmd3); pcraftsman2->AddCommand(pcmd6); pcraftsman2->Notify(); //释放资源 delete pgold; //释放资源 delete pcmd1; delete pcmd2; //释放资源 delete pcmd4; delete pcmd5; delete pcraftsman; //释放资源 delete pcmd3; delete pcmd6; delete pcraftsman2;
调用:
/*****************************************************************//** * \\file ConsoleDuCommandPattern.cpp * \\brief Command Pattern 命令模式 亦称:动作、事务、Action、Transaction、Command C++ 14 * 涂聚文 Geovin Du Visual Studio 2022 edit. * * \\author geovindu * \\date 26 May 2023 *********************************************************************/ // ConsoleDuCommandPattern.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 //2023年5月26日 涂聚文 Geovin Du Visual Studio 2022 edit. #define _UNICODE #include <iostream> #include <list> #include "GeovinDu.h" #ifdef _DEBUG //只在Debug(调试)模式下 #ifndef DEBUG_NEW #define DEBUG_NEW new(_NORMAL_BLOCK,__FILE__,__LINE__) //重新定义new运算符 #define new DEBUG_NEW #endif #endif using namespace std; using namespace DuCommonPattern; int main() _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);//程序退出时检测内存泄漏并显示到“输出”窗口 std::cout << "Hello World!!涂聚文 Geovin Du\\\\n"; GeovinDu geovin; geovin.displaySimple(); system("pause"); return 0; // 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单 // 调试程序: F5 或调试 >“开始调试”菜单 // 入门使用技巧: // 1. 使用解决方案资源管理器窗口添加/管理文件 // 2. 使用团队资源管理器窗口连接到源代码管理 // 3. 使用输出窗口查看生成输出和其他消息 // 4. 使用错误列表窗口查看错误 // 5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目 // 6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件 #define UNICODE
输出:
Hello World!!涂聚文 Geovin Du\\n制作一条金项链 制作一对黄金耳环 **** 制作一条金项链 制作一对黄金耳环 **** 制作一条金项链 制作一对黄金耳环 **** 制作一条金项链 制作一对黄金耳环 请按任意键继续. . .
命令模式(Command Pattern)
begin 2020年12月6日21:08:15
定义
Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.——《Design Patterns: Elements of Reusable Object-Oriented Software》
命令模式(Command),將一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或者记录请求日志,以及支持可撤销的操作。——《设计模式:可复用面向对象软件的基础》
图示
命令模式结构图:
角色
抽象命令角色(Command):
- 声明一个接口包含了执行的操作,如图中excute方法
具体命令角色(ConcreteCommand):
- 定义了接收者对象和命令的绑定
- 实现【抽象命令角色】定义的执行操作,具体实现是:在接收者调用对应的操作
客户端角色(Client):
- 创建一个【具体命令角色】,并设置它的接收者
调用者角色(Invoker):
- 要求命令执行请求,即调用Command.execute方法
接收者角色(receiver):
- 知道如何执行一个请求相关的操作,真正执行操作的人
代码示例
【记事本(notepad)】:通过命令模式实现记事本的复制(copy)、粘贴(paste)的功能
1、记事本打开TEXT文件之后,右键菜单(Menu)就是调用者,具体的菜单项目有复制、粘贴等
2、点击复制、粘贴命令,通过具体命令让接收者执行该命令,从TEXT复制到剪贴板,或从剪贴板粘贴到TEXT文件。
具体代码如下:
抽象命令角色(Command.java):
public interface Command {
void execute();
}
具体命令角色(CopyCommand.java、PasteCommand.java):
public class CopyCommand implements Command {
private Text receiver;
public CopyCommand(Text receiver) {
this.receiver = receiver;
}
@Override
public void execute() {
receiver.copy();
}
}
public class PasteCommand implements Command {
private Text receiver;
public PasteCommand(Text receiver) {
this.receiver = receiver;
}
@Override
public void execute() {
receiver.paste();
}
}
调用者角色(Menu.java):
public class Menu {
private static Map<String, Command> commandMap = new HashMap<>();
public void addMenuItem(String menuItemName, Command command) {
commandMap.put(menuItemName, command);
}
public void click(String menuItemName) {
commandMap.get(menuItemName).execute();
}
}
接收者(Text.java):
public class Text {
public void copy() {
System.out.println("复制成功");
}
public void paste() {
System.out.println("粘贴成功");
}
}
客户端角色(Menu.java):
public class Notepad {
public static void main(String[] args) {
// 打开文件
Text receiver = new Text();
// 初始化右键菜单
Menu menu = new Menu();
Command copyCommand = new CopyCommand(receiver);
Command pasteCommand = new PasteCommand(receiver);
menu.addMenuItem("copy", copyCommand);
menu.addMenuItem("paste", pasteCommand);
// 复制命令
menu.click("copy");
// 粘贴命令
menu.click("paste");
}
}
执行命令结果:
模式扩展
- 宏命令:使用组合模式将多个命令组合在一起
实现一个复制并粘贴命令,类似在Intellij IDEA里面的Windows系统快捷键是CTRL + D。
代码如下:
public class DuplicateCommand implements Command {
private List<Command> commandList;
public DuplicateCommand(Text receiver) {
commandList = new ArrayList<>();
commandList.add(new CopyCommand(receiver));
commandList.add(new PasteCommand(receiver));
}
@Override
public void execute() {
for(int i = 0; i < commandList.size(); i ++) {
commandList.get(i).execute();
}
}
}
- 撤销操作:使用备忘录模式保存命令历史,用来撤销
我们要记录TEXT当时的状态,如TEXT当前有什么内容
修改后的接收者角色,也是备忘录模式中的备忘录角色:
public class TextWithUndo {
private StringBuilder content;
public TextWithUndo(String text) {
this.content = new StringBuilder(text);
}
public void paste(String text) {
content.append(text);
System.out.println("粘贴成功");
}
public TextWithUndo createText() {
return new TextWithUndo(this.content.toString());
}
public String toString() {
return content.toString();
}
}
修改后的具体命令角色,备忘录的发起人角色:
public class PasteCommandWithUndo implements Command {
private TextWithUndo receiver;
private UndoCommandCaretaker commandCaretaker;
public PasteCommandWithUndo(TextWithUndo receiver, UndoCommandCaretaker commandCaretaker) {
this.receiver = receiver;
this.commandCaretaker = commandCaretaker;
}
@Override
public void execute() {
commandCaretaker.addText(this.createUndoReceiver());
String fromClipboard = " xxx ";
receiver.paste(fromClipboard);
}
public TextWithUndo createUndoReceiver() {
return new TextWithUndo(this.receiver.toString());
}
}
备忘录管理者角色:
public class UndoCommandCaretaker {
private List<TextWithUndo> receiverList = new ArrayList<>();
public TextWithUndo getText(int i) {
return receiverList.get(i);
}
public void addText(TextWithUndo text) {
receiverList.add(text);
}
}
修改后的客户端:
public class NotepadWithUndo {
public static void main(String[] args) {
// 打开文件
TextWithUndo receiver = new TextWithUndo("");
// 初始化右键菜单
Menu menu = new Menu();
// 备忘录
UndoCommandCaretaker commandCaretaker = new UndoCommandCaretaker();
Command pasteCommand = new PasteCommandWithUndo(receiver, commandCaretaker);
menu.addMenuItem("paste", pasteCommand);
// 粘贴命令
menu.click("paste");
menu.click("paste");
System.out.println(receiver.toString()); // 输出: xxx xxx
receiver = commandCaretaker.getText(1);
System.out.println(receiver.toString()); // 输出: xxx
}
}
这样子结合备忘录模式就可以实现撤销操作
使用场景
- 请求调用者和请求接收者解耦,使得调用者和接收者不直接交互
- 在不同的时间执行请求,将请求排队和执行请求(本文并未给出例子,可自行探究)
- 支持撤销(undo)和恢复(redo)操作(本文在模式扩展时有此代码样例)
- 支持将一组操作组合在一起,即宏命令(上文有样例)
优点
- 降低耦合
- 符合开闭原则,很容易就可以添加新的命令
缺点
- 可能会产生过多的具体命令类
总结
命令模式的动机是为了消除请求调用者和请求接收者直接的耦合,使得请求调用者无需知道请求调用者。命令模式支持对请求的记录和排队、撤销等操作,也支持一个宏命令,一次执行多个命令。
2021年5月16日14:25:27
以上是关于cpp: Command Pattern的主要内容,如果未能解决你的问题,请参考以下文章
strategy pattern & command pattern