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

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

一、基本概念

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

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

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

二、程序设计

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

1.Receiver
//"Receiver_Array.h"
#pragma once
#include
#include
#include
template 
class Receiver_Array
{
private:
	std::vector* myArry;
public:
	~Receiver_Array()
	{
	}
	Receiver_Array() 
	{
		myArry = new std::vector();
	}
	Receiver_Array(std::vector* _myArry)
	{
		if (_myArry)
		{
			myArry = new std::vector(*_myArry);
		}
		else
		{
			myArry = new std::vector();
		}
	}
	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 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
//Command.h
#pragma once
#include "Receiver_Array.h"
#include
#include 
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 AddCommand:public Command
{
private:
	std::shared_ptr> m_Receiver_Array;
	T m_Val;
public:
	AddCommand() {}
	AddCommand(std::shared_ptr> _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 DelCommand :public Command
{
private:
	std::shared_ptr>  m_Receiver_Array;
	T m_Val;
public:
	DelCommand() {}
	DelCommand(std::shared_ptr>  _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 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 ModifyCommand :public Command
{
private:
	std::shared_ptr>  m_Receiver_Array;
	T m_Val;
public:
	ModifyCommand() {}
	ModifyCommand(std::shared_ptr>  _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 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 SeekCommand :public Command
{
private:
	std::shared_ptr>  m_Receiver_Array;
public:
	SeekCommand():m_bCanRevert(false) {}
	SeekCommand(std::shared_ptr>  _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 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
//CommandMgr.h
#pragma once
#include 
#include 
class Command;
class CommandMgr
{
private:
	std::stack> m_stkUndo;//undo栈
	std::stack> m_stkRedo;//redo栈
public:
	CommandMgr();
	~CommandMgr();
	void execute(std::shared_ptr command) noexcept;
	void* executeOther(std::shared_ptr command)noexcept;
	void undo() noexcept;
	void redo() noexcept;
};

//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) 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) 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 = 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 = 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函数

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

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

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

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

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

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

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

	printf("ok");
}

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

你可能感兴趣的:(设计模式学习笔记,设计模式,命令模式)