设计模式--命令模式的简单例子

引入:以一个对数组的增删改查为例。通过命令模式可以对数组进行增删改查以及撤销回滚。

一、基本概念

命令模式有多种分法,在本文中主要分为CommandMgr、Command、Receiver.

CommandMgr主要用于控制命令执行等操作、Command为具体的命令、Receiver为命令具体要操作的对象。

总而言之,增删改查就是具体的Command、Receiver就是数组、CommandMgr负责控制命令的执行与回滚等。

二、程序设计

以下代码可从github下载:GitHub - laizilianglaiziliang/LearnCommandProject

1.Receiver
cpp 复制代码
//"Receiver_Array.h"
#pragma once
#include<vector>
#include<optional>
#include<iostream>
template <class T>
class Receiver_Array
{
private:
	std::vector<T>* myArry;
public:
	~Receiver_Array()
	{
	}
	Receiver_Array() 
	{
		myArry = new std::vector<T>();
	}
	Receiver_Array(std::vector<T>* _myArry)
	{
		if (_myArry)
		{
			myArry = new std::vector<T>(*_myArry);
		}
		else
		{
			myArry = new std::vector<T>();
		}
	}
	bool add(const int pos, const T& val)
	{
		if (errorCheck(pos))
		{
			return false;
		}
		myArry->insert(pos + myArry->begin(), val);
		return true;
	}
	bool del(const int pos)
	{
		if (errorCheck(pos))
		{
			return false;
		}
		myArry->erase(pos + myArry->begin());
		return true;
	}
	bool modify(const int pos, const T& val)
	{
		if (errorCheck(pos))
		{
			return false;
		}
		myArry->erase(pos + myArry->begin());
		return true;
	}
	std::optional<T> seek(const int pos)
	{
		if (errorCheck(pos))
		{
			return std::nullopt;
		}
		return (*myArry)[pos];
	}
	bool errorCheck(const int pos)
	{
		if (pos >= myArry->size())
		{
			printf("  Operation Failed.Array Bounds Errors.  ");
			return true;
		}
		return false;
	}
	void display()
	{
		for (int i = 0; i < myArry->size(); ++i)
		{
			std::cout << (*myArry)[i] << "  ";
		}
		std::cout << std::endl;
	}
};

在本例子中Receiver_Array类是一个模板类,可支持不同类型的数组。同时实现了对数组进行增删改查,为不同的命令类提供了基础的功能。

2.Command
cpp 复制代码
//Command.h
#pragma once
#include "Receiver_Array.h"
#include<optional>
#include <memory>
class Command
{
public:		
	int m_iPos;
	bool m_bCanRevert;
public:
	Command(int _pos) : m_iPos(_pos), m_bCanRevert(true)
	{
	}
	virtual ~Command(){}
	virtual bool  execute() = 0;
	virtual void* executeOther()
	{
		return nullptr;
	}
	virtual bool  undo() = 0;
	virtual bool  redo() = 0;
};
template <class T>
class AddCommand:public Command
{
private:
	std::shared_ptr<Receiver_Array<T>> m_Receiver_Array;
	T m_Val;
public:
	AddCommand() {}
	AddCommand(std::shared_ptr<Receiver_Array<T>> _receiver_Array, int _pos,const T& _val) :Command( _pos), m_Receiver_Array(_receiver_Array), m_Val(_val)
	{		
	}
	virtual ~AddCommand() 
	{
		//if (m_Receiver_Array)
		//{
		//	m_Receiver_Array->destory();
		//	m_Receiver_Array = nullptr;
		//}
	}
	bool execute() override
	{
		try
		{
			if (this->m_Receiver_Array->add(this->m_iPos, m_Val))
			{
				printf("  Add Success.");
				return true;
			}
			printf("  Add Fail.");
			return false;
		}
		catch(...)
		{
			printf("  Add Fail.");
			return false;
		}
		return true; 
	}
	virtual bool undo() override
	{
		try
		{
			if (this->m_Receiver_Array->del(this->m_iPos))
			{
				printf("  Undo Success.");
				return true;
			}
		}
		catch (...)
		{
		}
		printf("  Undo Fail.");
		return false;
	}
	virtual bool redo() override
	{
		if (execute())
		{
			printf("  Redo Success.");
			return true;
		}
		printf("  Redo Fail.");
		return false;
	}
};
template <class T>
class DelCommand :public Command
{
private:
	std::shared_ptr<Receiver_Array<T>>  m_Receiver_Array;
	T m_Val;
public:
	DelCommand() {}
	DelCommand(std::shared_ptr<Receiver_Array<T>>  _receiver_Array, int _pos) :Command(_pos), m_Receiver_Array(_receiver_Array)
	{
	}
	virtual ~DelCommand()
	{
		//if (m_Receiver_Array)
		//{
		//	m_Receiver_Array->destory();
		//	m_Receiver_Array = nullptr;
		//}
	}
	bool execute() override
	{
		try
		{
			std::optional<T> val = m_Receiver_Array->seek(m_iPos);
			if (val!=std::nullopt && this->m_Receiver_Array->del(this->m_iPos))
			{
				printf("  Del Success.");
				m_Val = val.value();
				return true;
			}
			printf("  Del Fail.");
			return false;
		}
		catch (...)
		{
			printf("  Del Fail.");
			return false;
		}
		return true;
	}
	virtual bool undo() override
	{
		try
		{
			if (this->m_Receiver_Array->add(this->m_iPos, m_Val))
			{
				printf("  Undo Success.");
				return true;
			}
		}
		catch (...)
		{
		}
		printf("  Undo Fail.");
		return false;
	}
	virtual bool redo() override
	{
		if (execute())
		{
			printf("  Redo Success.");
			return true;
		}
		printf("  Redo Fail.");
		return false;
	}
};
template <class T>
class ModifyCommand :public Command
{
private:
	std::shared_ptr<Receiver_Array<T>>  m_Receiver_Array;
	T m_Val;
public:
	ModifyCommand() {}
	ModifyCommand(std::shared_ptr<Receiver_Array<T>>  _receiver_Array, int _pos,const T& _val) :Command(_pos), m_Receiver_Array(_receiver_Array), m_Val(_val)
	{
	}
	virtual ~ModifyCommand()
	{
		//if (m_Receiver_Array)
		//{
		//	m_Receiver_Array->destory();
		//	m_Receiver_Array = nullptr;
		//}
	}
	bool execute() override
	{
		try
		{
			std::optional<T> val = this->m_Receiver_Array->seek(m_iPos);//判断m_iPos是合法的
			if (val != std::nullopt && this->m_Receiver_Array->modify(this->m_iPos, m_Val))
			{
				printf("  Modify Success.");
				m_Val = val.has_value();//用于undo redo
				return true;
			}			
			printf("  Modify Fail.");
			return false;
		}
		catch (...)
		{
			printf("  Modify Fail.");
			return false;
		}
		return true;
	}
	virtual bool undo() override
	{
		try
		{
			if (execute())
			{
				printf("  Undo Success.");
				return true;
			}
		}
		catch (...)
		{
		}
		printf("  Undo Fail.");
		return false;
	}
	virtual bool redo() override
	{
		if (execute())
		{
			printf("  Redo Success.");
			return true;
		}
		printf("  Redo Fail.");
		return false;
	}
};
template <class T>
class SeekCommand :public Command
{
private:
	std::shared_ptr<Receiver_Array<T>>  m_Receiver_Array;
public:
	SeekCommand():m_bCanRevert(false) {}
	SeekCommand(std::shared_ptr<Receiver_Array<T>>  _receiver_Array, int _pos) :Command(_pos), m_Receiver_Array(_receiver_Array)
	{
		m_bCanRevert = false;
		//, m_bCanRevert(false)
	}
	virtual ~SeekCommand()
	{
		//if (m_Receiver_Array)
		//{
		//	m_Receiver_Array->destory();
		//	m_Receiver_Array = nullptr;
		//}
	}
	bool execute() override
	{
		return false;
	}
	virtual void* executeOther() override
	{
		try
		{
			std::optional<T> val = m_Receiver_Array->seek(m_iPos);
			if (val == std::nullopt)
			{
				printf("  Seek Fail.");
				return nullptr;
			}
			printf("  Seek Success.");
			T* ret = new T();
			*ret = val.value();
			return ret;
		}
		catch (...)
		{
		}
		printf("  Seek Fail.");
		return nullptr;
	}
	virtual bool undo() override
	{
		printf("  Undo Fail.");
		return false;
	}
	virtual bool redo() override
	{
		printf("  Redo Fail.");
		return false;
	}
};

**1)**Command类是命令基类。本来也想将Command设计成模板类,但是后面想想感觉不太好,因为Command设计成模板类会影响到CommandMgr也变成模板类。如果Command类是模板类,要注意其属性如果在派生类中要用的话要用this指针去访问,否则会出现找不到标识符的问题。

可参考:

C++模板类中,派生类使用基类中数据或方法报"找不到标识符"_c++头文件引用其他类提示找不到符号-CSDN博客

**2)**Command类中有个m_bCanRevert属性用于判断该命令是否可以被撤销回滚,因为并不是所有的命令都支持撤销回滚,比如例子中的SeekCommand。

**3)**Command类中有个executeOther,因为SeekCommand执行后需要返回一个值,是特殊的命令,因此executeOther用于执行特殊的命令

**4)**其他的Command派生类依赖于Receiver_Array类,可能会出现多个类依赖于同一个Receiver_Array类对象的情况,因此把Receiver_Array类成员变量设置为智能指针方便内存的释放

**5)**其他的主要就是实现每个Command类的execute、undo、redo方法,这个直接看逻辑就能理解。

3.CommandMgr
cpp 复制代码
//CommandMgr.h
#pragma once
#include <stack>
#include <memory>
class Command;
class CommandMgr
{
private:
	std::stack<std::shared_ptr<Command>> m_stkUndo;//undo栈
	std::stack<std::shared_ptr<Command>> m_stkRedo;//redo栈
public:
	CommandMgr();
	~CommandMgr();
	void execute(std::shared_ptr<Command> command) noexcept;
	void* executeOther(std::shared_ptr<Command> command)noexcept;
	void undo() noexcept;
	void redo() noexcept;
};
cpp 复制代码
//CommandMgr.cpp
#include "CommandMgr.h"
#include "Command.h"
CommandMgr::CommandMgr()
{
}
CommandMgr::~CommandMgr()
{
	while (!m_stkRedo.empty())
	{
		m_stkRedo.pop();
	}
	while (!m_stkUndo.empty())
	{		
		m_stkUndo.pop();
	}
}
void CommandMgr::execute(std::shared_ptr<Command> command) noexcept
{
	if (command->execute())
	{
		printf("  Command Execute Success\n\n");
		if (command->m_bCanRevert)
		{
			m_stkUndo.push(command);
		}
	}
	else
	{
		printf("  Command Execute Fail\n\n");
	}
}

void* CommandMgr::executeOther(std::shared_ptr<Command> command) noexcept
{
	void* val = command->executeOther();
	if (val)
	{
		printf("  Command Execute Success\n\n");
		if (command->m_bCanRevert)
		{
			m_stkUndo.push(command);
		}
		return val;
	}
	else
	{
		printf("  Command Execute Fail\n\n");
	}
	return nullptr;
}

void CommandMgr::undo() noexcept
{
	if (m_stkUndo.empty())
	{
		return;
	}
	std::shared_ptr<Command> command = m_stkUndo.top();
	if (command && command->m_bCanRevert && command->undo())
	{		
		m_stkUndo.pop();
		m_stkRedo.push(command);
		printf("  Command Undo Success\n\n");
	}
	else
	{
		printf("  Command Undo Fail\n\n");
	}
}

void CommandMgr::redo() noexcept
{
	if (m_stkRedo.empty())
	{
		return;
	}
	std::shared_ptr<Command> command = m_stkRedo.top();
	if (command && command->m_bCanRevert && command->redo())
	{
		m_stkUndo.push(command);
		printf("  Command Redo Success\n\n");
	}
	else
	{
		printf("  Command Redo Fail\n\n");
	}
}

**1)**CommandMgr主要用于管理命令,用来操作具体的命令的调用控制、undo、redo控制。

**2)**因为Command类型对象的内存不太好管理,因此也使用了智能指针。

**3)**里面的undo、redo主要通过栈来实现。当命令execute过后便会添加到undo栈,接下来的undo、redo主要就是对undo栈和redo栈进行互相倒腾。

4.main函数

当做完上面的工作就能对数组进行方便的增删改查了,还可以撤销回退哦。

cpp 复制代码
// LearnCommandProject.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。

#include <iostream>
#include <vector>
#include "Command.h"
#include "Receiver_Array.h"
#include "CommandMgr.h"
#include <memory>
int main()
{
	std::vector<int> vec{ 1,2,3 };
	std::shared_ptr<Receiver_Array<int>> receiver_Array(new Receiver_Array<int>(&vec));

	std::shared_ptr<AddCommand<int>> addCommand(new AddCommand<int>(receiver_Array, 3, 4));
	CommandMgr commandMgr;
	commandMgr.execute(addCommand);
	commandMgr.undo();
	commandMgr.redo();
	receiver_Array->display();

	std::shared_ptr<SeekCommand<int>> seekCommand(new SeekCommand<int>(receiver_Array, 1));
	int* val= (int*)(commandMgr.executeOther(seekCommand));
	receiver_Array->display();
	delete val;
	val = nullptr;

	std::shared_ptr<DelCommand<int>> delCommand(new DelCommand(receiver_Array, 1));
	commandMgr.execute(delCommand);
	commandMgr.undo();
	commandMgr.redo();
	receiver_Array->display();

	std::shared_ptr<ModifyCommand<int>> modifyCommand(new ModifyCommand(receiver_Array, 2, 2));
	commandMgr.execute(modifyCommand);
	commandMgr.undo();
	commandMgr.redo();
	receiver_Array->display();

	printf("ok");
}

.上面的代码可能还有设计不好的地方,欢迎批评指正。

相关推荐
晨米酱12 小时前
JavaScript 中"对象即函数"设计模式
前端·设计模式
数据智能老司机17 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机18 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机18 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机18 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
使一颗心免于哀伤18 小时前
《设计模式之禅》笔记摘录 - 21.状态模式
笔记·设计模式
数据智能老司机2 天前
精通 Python 设计模式——创建型设计模式
python·设计模式·架构
数据智能老司机2 天前
精通 Python 设计模式——SOLID 原则
python·设计模式·架构
烛阴2 天前
【TS 设计模式完全指南】懒加载、缓存与权限控制:代理模式在 TypeScript 中的三大妙用
javascript·设计模式·typescript
李广坤2 天前
工厂模式
设计模式