C++设计模式之命令模式(command)(行为型)

一 定义

命令模式(Command):将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求进行排队或记录请求日志,以及支持可撤销的操作。

    其本质是对命令的抽象和封装,把通常的命令发起——>接收到命令并执行过程,通过增加一个中间角色变为命令发起——>中介负责先接收命令再转发——>接收到命令再执行过程,简而言之,就是命令发起者可以将很多命令放进中间角色, 它并不知道功能是如何实现的,只负责转发给真正的命令执行者。

二 ULM图

 

角色说明:

抽象命令类(Command): 声明执行操作的接口。调用接收者相应的操作,以实现执行的方法Execute。
具体命令类(ConcreteCommand): 创建一个具体命令对象并设定它的接收者。通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。 
调用者(Invoker): 要求该命令执行这个请求。通常会持有命令对象,可以持有很多的命令对象。
接收者(Receiver): 知道如何实施与执行一个请求相关的操作。任何类都可能作为一个接收者,只要它能够实现命令要求实现的相应功能。 
客户类(Client): 创建具体的命令对象,并且设置命令对象的接收者。真正使用命令的客户端是从Invoker来触发执行。

Command模式优点:

1) 降低系统的耦合度:Command模式将调用操作的对象与知道如何实现该操作的对象解耦。

2) Command是头等的对象。它们可像其他的对象一样被操纵和扩展。

3) 组合命令:你可将多个命令装配成一个组合命令,即可以比较容易地设计一个命令队列和宏命令。一般说来,组合命令是Composite模式的一个实例。

4) 增加新的Command很容易,因为这无需改变已有的类。

5)可以方便地实现对请求的Undo和Redo。

命令模式的缺点:

使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个命令都需要设计一个具体命令类,因此某些系统可能需要大量具体命令类,这将影响命令模式的使用。

实例1:

烧烤店有烤羊肉串和烤鸡翅。对于就餐的客户来说,只需要告诉服务员需要点什么,而不关心点的餐怎么做。

 

 

代码实现:

#include <iostream>
#include <string>
#include <list>

//Receiver:此处为烤羊肉串者
class Barbecuer
{
public:
	void BakeMutton()
	{
		std::cout << "烤羊肉串喽!" << std::endl;
	}
	void BakeChickenWing()
	{
		std::cout << "烤鸡翅喽!" << std::endl;
	}
};

//Command类,抽象命令
class Command
{
protected:
	Barbecuer* receiver;
public:
	Command(Barbecuer* receiver)
	{
		this->receiver = receiver;
	}
	virtual void ExcuteCommand() = 0;
};

//ConcreteCommand类,具体命令
class BakeMuttonCommand :public Command
{
public:
	BakeMuttonCommand(Barbecuer* receiver) :Command(receiver) {}
	void ExcuteCommand()
	{
		receiver->BakeMutton();
	}
};

//ConcreteCommand类,具体命令
class BakeChickenWingCommand :public Command
{
public:
	BakeChickenWingCommand(Barbecuer* receiver) :Command(receiver) {}
	void ExcuteCommand()
	{
		receiver->BakeChickenWing();
	}
};

//Invoker:此处为Waiter服务员
class Waiter
{
private:
	std::list<Command*>* orders;
public:
	Waiter()
	{
		orders = new std::list<Command*>;
	}
	~Waiter()
	{
		delete orders;
	}

	//设置订单
	void SetOrder(Command* command)
	{
		//判断命令的类型并分别做不同的处理
		if (typeid(*command) == typeid(BakeChickenWingCommand))
		{
			std::cout << "日志:服务员:鸡翅没有了,请点别的烧烤!" << std::endl;
		}
		else if (typeid(*command) == typeid(BakeMuttonCommand))
		{
			orders->push_back(command);
			std::cout << "日志:增加订单:命令模式.烤羊肉串  " << std::endl;
		}
		else
		{
			std::cout << "日志:暂时没有该服务!" << std::endl;
		}
	}

	//通知全部执行
	void Notify()
	{
		std::list<Command*>::iterator it;
		for (it = orders->begin(); it != orders->end(); it++)
		{
			(*it)->ExcuteCommand();
		}
	}

	//取消订单,这里仅仅是为了演示,真实需要根据订单号来取消
	void CancelOrder(Command* command)
	{
		
		if (typeid(*command) == typeid(BakeChickenWingCommand))
		{
			orders->remove_if([](const auto& item) { return typeid(*item) == typeid(BakeChickenWingCommand); });
			std::cout << "取消订单:烤鸡翅" << std::endl;
		}
		else if (typeid(*command) == typeid(BakeMuttonCommand))
		{
			orders->remove_if([](const auto& item) { return typeid(*item) == typeid(BakeChickenWingCommand); });
			std::cout << "取消订单:烤羊肉串" << std::endl;
		}
	}
};

int main()
{
	//开店前的准备
	Barbecuer* boy = new Barbecuer();

	Command* bakeMuttonCommand = new BakeMuttonCommand(boy);
	Command* bakeChickenWingCommand = new BakeChickenWingCommand(boy);

	Waiter* girl = new Waiter();

	//开门营业,顾客点菜
	girl->SetOrder(bakeMuttonCommand);
	girl->SetOrder(bakeChickenWingCommand);
	girl->CancelOrder(bakeChickenWingCommand);

	//点菜完毕,通知厨房
	girl->Notify();

	delete boy, bakeMuttonCommand, bakeMuttonCommand, bakeChickenWingCommand;
	
	return 0;
}

运行结果:

 

实例2:

电视机遥控器 :

电视机是请求的接收者, 遥控器是请求的发送者, 遥控器上有一些按钮,不同的按钮对应电视机的不同操作。抽象命令角色由一个命令接口来扮演, 有三个具体的命令类实现了抽象命令接口,这三个具体命令类分别代表三种操作:打开电视机、关闭电视机和切换频道。

 显然,电视机遥控器就是一个典型的命令模式应用实例。

ULM图:

 代码实现:

#include <iostream>
#include <memory>

//Receiver: Television
class Television 
{
public:
	void open() 
	{
		std::cout << "打开电视机!" << std::endl;
	}

	void close() 
	{
		std::cout << "关闭电视机!" << std::endl;
	}

	void changeChannel() 
	{
		std::cout << "切换电视频道!" << std::endl;
	}
};

//AbstartCommand
class AbstractCommand 
{
public:
	virtual void execute() = 0;
};

//Concrete commmand: TVOpenCommand
class TVOpenCommand : public AbstractCommand 
{
public:
	TVOpenCommand(std::shared_ptr<Television> tv)
	{
		this->tv = tv;
	}

	void execute() 
	{
		tv->open();
	}

private:
	std::shared_ptr<Television> tv;
};

//Concrete command: TVCloseCommmand
class TVCloseCommand : public AbstractCommand 
{
public:
	TVCloseCommand(std::shared_ptr<Television> tv) 
	{
		this->tv = tv;
	}

	void execute() 
	{
		tv->close();
	}

private:
	std::shared_ptr<Television> tv;
};

//Concrete command: TVChangeCommmand
class TVChangeCommand : public AbstractCommand 
{
public:
	TVChangeCommand(std::shared_ptr<Television> tv) 
	{
		this->tv = tv;
	}

	void execute() 
	{
		tv->changeChannel();
	}

private:
	std::shared_ptr<Television> tv;
};

//Invoker: Controller
class Controller 
{
public:
	Controller(std::shared_ptr<AbstractCommand> openCommand,
		std::shared_ptr<AbstractCommand> closeCommand,
		std::shared_ptr<AbstractCommand> changeCommand) {
		this->openCommand = openCommand;
		this->closeCommand = closeCommand;
		this->changeCommand = changeCommand;
	}

	void open()
	{
		openCommand->execute();
	}

	void change() 
	{
		changeCommand->execute();
	}

	void close() 
	{
		closeCommand->execute();
	}
private:
	std::shared_ptr<AbstractCommand> openCommand;
	std::shared_ptr<AbstractCommand> closeCommand;
	std::shared_ptr<AbstractCommand> changeCommand;
};

//客户端测试
int main(void) {
	// 接收者电视机 
	std::shared_ptr<Television> tv = std::make_shared<Television>();

	// 命令 
	std::shared_ptr<AbstractCommand> openCommand = std::make_shared<TVOpenCommand>(tv);
	std::shared_ptr<AbstractCommand> closeCommand = std::make_shared<TVCloseCommand>(tv);
	std::shared_ptr<AbstractCommand> changeCommand = std::make_shared<TVChangeCommand>(tv);

	// 调用者
	std::shared_ptr<Controller> controll = std::make_shared<Controller>(openCommand, closeCommand, changeCommand);

	// 测试 
	controll->open();
	controll->change();
	controll->close();

	return 0;
}

 运行结果:

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章