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

命令模式(Command Pattern)

命令模式(Command Pattern)

Command Pattern的简单介绍

设计模式 - 命令模式(command pattern) 具体解释

设计模式 - 命令模式(command pattern) 多命令 具体解释