设计模式系列(九)命令模式(Command Pattern)

设计模式系列(九)命令模式(Command Pattern)


    命令模式是将请求封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象,命令模式也支持可撤销的操作。命令模式将行为请求者与行为执行者解耦,从而实现了松耦合的原则。例如:去餐厅点餐的时候,顾客只负责点餐,然后交给服务员,服务员将订单交给厨师去做,最后顾客开始享用美味的饭菜,但是整个过程顾客只关心自己点了什么,而不关心餐厅到底是按照怎样的流程和方法做出这些菜的。再比如:遥控器上有很多按钮,每个按钮都相当于一个命令或者多个命令,当我们按下开机的时候,我们只关心电视是否开机了,而不关心也不知道到底是怎么开机的。这些例子中,顾客点菜以及用遥控器的人都是只请求一些行为,而具体的行为执行则是由其他执行的。

    命令模式中通常存在以下5个角色:

(1)Command角色:这个角色是接口或者抽象类,一般只有一个execute()函数留给子类去实现,有时候支持撤销操作的话会加上undo()函数,这个角色是所有具体命令的父类或者说是一个命令接口,并没有任何具体实现;

(2)ConcreteCommand角色:这个角色是抽象命令角色的具体实现类,实现其中的execute()函数,比如说在遥控器上的开机命令、关机命令等,这个具体命令类中,一般都需要有一个Receiver,用来执行真正的命令,再比如餐厅的订单就是命令

(3)Receiver角色:接收者,真正执行命令的对象,任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能,比如餐厅的厨师就是接收顾客的命令去做菜;

(4)Invoker角色:要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口,例如餐厅的服务员就是接收顾客的命令然后使用ConcreteCommand对象交给厨师,再比如遥控器本身就是一个Invoker;

(5)Client角色:创建具体的命令对象,并且设置命令对象的接收者。注意这个不是我们常规意义上的客户端,而是在组装命令对象和接收者,或许,把这个Client称为装配者会更好理解,因为真正使用命令的客户端是从Invoker来触发执行,其创建一组命令然后交给Invoker去触发,最终通过ConcreteCommandReceiver去执行

    上面角色的大致关系是:Client创建一个ConcreteCommand对象并指定他的Receiver对象;然后某个Invoker对象存储该ConcreteCommand对象;该Invoker通过调用Command对象的Execute操作来提交一个请求。若该命令是可撤销的,ConcreteCommand就在执行Execute操作之前存储当前状态以用于取消该命令;ConcreteCommand对象对调用它的Receiver的一些操作以执行该请求。

    命令模式的优点是:

(1)降低对象之间的耦合度;
(2)新的命令可以很容易地加入到系统中;
(3)可以比较容易地设计一个组合命令;
(4)调用同一方法实现不同的功能。

    命令模式的缺点是:

    由于命令模式需要创建许许多多的具体命令类,每一个具体的命令都要创建一个对应的命令类,所以会导致系统中存在大量的具体命令类,使得系统对这个具体命令类的管理比较麻烦。比如:电视机遥控器上很多按钮,每个按钮都对应一个具体命令类,遥控器上有很多按钮,从而就需要定义很多具体的命令类。

    下面我们来看一个例子,这个例子是关于类似于遥控器的一个控制器,该例由三个文件组成,依次是:CommandPattern.h、CommandPattern.cpp、CommandPatternTest.cpp。

// 命令模式
// CommandPattern.h文件

#ifndef COMMAND
#define COMMAND

#include <iostream>
#include <iomanip>
#include <string>
#include <vector>

using std::string;
using std::cout;
using std::endl;
using std::vector;

// 命令按钮个数
const int g_num = 7;		

// 命令模式中的命令角色的抽象类
class Command
{
public:
	Command(){}
	virtual ~Command(){}

	virtual void execute() = 0;
	virtual void undo() = 0;
};

// 一个用来初始化命令的具体命令类
class NoCommand : public Command
{
	void execute(){}
	void undo(){}
};

// 命令模式中的invoker
class RemoteControl
{
public:
	RemoteControl()
	{
		for (int i = 0; i < g_num; i++)
		{
			onCommands[i]  = new NoCommand();
			offCommands[i] = new NoCommand();
		}

		undoCommand = new NoCommand();
	}

	~RemoteControl()
	{
		for (int i = 0; i < g_num; i++)
		{
			if (onCommands[i] != NULL && typeid(*onCommands[i]) == typeid(NoCommand))
			{
				delete onCommands[i];
				onCommands[i] = NULL;
			}

			if (offCommands[i] != NULL && typeid(*offCommands[i]) == typeid(NoCommand))
			{
				delete offCommands[i];
				offCommands[i] = NULL;
			}
		}

		if (undoCommand != NULL && typeid(*undoCommand) == typeid(NoCommand))
		{
			delete undoCommand;
			undoCommand = NULL;
		}
	}

	void setCommand(int slot, Command* onCommand, Command* offCommand);
	void onButtonWasPushed(int slot);
	void offButtonWasPushed(int slot);
	void undoButtonWasPushed();
	string toString();
private:
	Command* onCommands[g_num];
	Command* offCommands[g_num];
	Command* undoCommand;
};

// 命令模式中的Reciver
class Light 
{
public:
	Light(string location) {
		this->location = location;
	}

	void on();
	void off();
	void dim(int level);
	int getLevel();
private:
	string location;
	int level;
};

class Stereo 
{
public:
	Stereo(string location) 
	{
		this->location = location;
	}

	void on();
	void off();
	void setCD();
	void setDVD();
	void setRadio();
	void setVolume(int volume);
private:
	string location;
};

class CeilingFan 
{
public:
	static const int HIGH;
	static const int MEDIUM;
	static const int LOW;
	static const int OFF;

	CeilingFan(string location) 
	{
		this->location = location;
	}

	void high();
	void medium();
	void low();
	void off();
	int getSpeed();
private:
	string location;
	int speed;
};

// 命令模式中具体的命令类
class LightOnCommand : public Command
{
public:
	LightOnCommand(Light& light) : light(light){}

	void execute();
	void undo();
private:
	Light& light;
};

class LightOffCommand : public Command
{
public:
	LightOffCommand(Light& light) : light(light){}

	void execute();
	void undo();
private:
	Light& light;
};

class LivingroomLightOnCommand : public Command
{
public:
	LivingroomLightOnCommand(Light& light) : light(light){}

	void execute();
	void undo();
private:
	Light& light;
};

class LivingroomLightOffCommand : public Command
{
public:
	LivingroomLightOffCommand(Light& light) : light(light){}

	void execute();
	void undo();
private:
	Light& light;
};

class StereoOnCommand : public Command
{
public:
	StereoOnCommand(Stereo& stereo) : stereo(stereo){}

	void execute();
	void undo();
private:
	Stereo& stereo;
};

class StereoOffCommand : public Command
{
public:
	StereoOffCommand(Stereo& stereo) : stereo(stereo){}

	void execute();
	void undo();
private:
	Stereo& stereo;
};

class StereoOnWithCDCommand : public Command
{
public:
	StereoOnWithCDCommand(Stereo& stereo) : stereo(stereo){}

	void execute();
	void undo();
private:
	Stereo& stereo;
};

class CeilingFanHighCommand : public Command
{
public:
	CeilingFanHighCommand(CeilingFan& ceilingFan) : ceilingFan(ceilingFan){}

	void execute();
	void undo();
private:
	CeilingFan& ceilingFan;
	int prevSpeed;
};

class CeilingFanMediumCommand : public Command
{
public:
	CeilingFanMediumCommand(CeilingFan& ceilingFan) : ceilingFan(ceilingFan){}

	void execute();
	void undo();
private:
	CeilingFan& ceilingFan;
	int prevSpeed;
};

class CeilingFanOffCommand : public Command
{
public:
	CeilingFanOffCommand(CeilingFan& ceilingFan) : ceilingFan(ceilingFan){}

	void execute();
	void undo();
private:
	CeilingFan& ceilingFan;
	int prevSpeed;
};

// 这个是命令模式中常用的类:命令宏,用来执行一组命令
class MacroCommand : public Command
{
public:
	MacroCommand(vector<Command*> commands)
	{
		this->commands = commands;
	}

	void execute();
	void undo();
private:
	vector<Command*> commands;
};

#endif

// CommandPattern.cpp文件

#include "CommandPattern.h"

// 命令模式中的invoker
void RemoteControl::setCommand(int slot, Command* onCommand, Command* offCommand)
{
	if (onCommands[slot] != NULL && typeid(*onCommands[slot]) == typeid(NoCommand))
	{
		delete onCommands[slot];
	}

	if (offCommands[slot] != NULL && typeid(*offCommands[slot]) == typeid(NoCommand))
	{
		delete offCommands[slot];
	}

	onCommands [slot] = onCommand;
	offCommands[slot] = offCommand;
}

void RemoteControl::onButtonWasPushed(int slot)
{
	onCommands[slot]->execute();

	if (undoCommand != NULL && typeid(*undoCommand) == typeid(NoCommand))
	{
		delete undoCommand;
	}
	undoCommand = onCommands[slot];
}

void RemoteControl::offButtonWasPushed(int slot)
{
	offCommands[slot]->execute();

	if (undoCommand != NULL && typeid(*undoCommand) == typeid(NoCommand))
	{
		delete undoCommand;
	}
	undoCommand = onCommands[slot];
}

void RemoteControl::undoButtonWasPushed()
{
	undoCommand->undo();
}

string RemoteControl::toString()
{
	string stringBuff;
	char buf[2];
	stringBuff += "\n------ Remote Control -------\n";
	for (int i = 0; i < g_num; i++) 
	{

		stringBuff += "[slot ";
		sprintf_s(buf, "%d", i);
		stringBuff += buf;
		stringBuff += "] ";
		stringBuff += typeid(*onCommands[i]).name();
		stringBuff += "    ";
		stringBuff += typeid(*offCommands[i]).name();
		stringBuff +=  "\n";
	}
	stringBuff += "[undo] ";
	stringBuff += typeid(*undoCommand).name();
	stringBuff += "\n";
	return stringBuff;
}

// 命令模式中的Reciver
// Light
void Light::on()
{
	level = 100;
	cout << "Light is on" << endl;
}

void Light::off() {
	level = 0;
	cout << "Light is off" << endl;
}

void Light::dim(int level)
{
	this->level = level;
	if (level == 0)
	{
		off();
	}
	else
	{
		cout << "Light is dimmed to " << level << "%" << endl;
	}
}

int Light::getLevel()
{
	return level;
}

// Stereo
void Stereo::on()
{
	cout << location << " stereo is on" << endl;
}

void Stereo::off()
{
	cout << location << " stereo is off" << endl;
}

void Stereo::setCD()
{
	cout << location << " stereo is set for CD input" << endl;
}

void Stereo::setDVD()
{
	cout << location << " stereo is set for DVD input" << endl;
}

void Stereo::setRadio()
{
	cout << location << " stereo is set for Radio" << endl;
}

void Stereo::setVolume(int volume)
{
	// code to set the volume
	// valid range: 1-11 (after all 11 is better than 10, right?)
	cout << location << " Stereo volume set to " << volume << endl;
}

// CeilingFan
const int CeilingFan::HIGH = 3;
const int CeilingFan::MEDIUM = 2;
const int CeilingFan::LOW = 1;
const int CeilingFan::OFF = 0;

void CeilingFan::high()
{
	// turns the ceiling fan on to high
	speed = HIGH;
	cout << location + " ceiling fan is on high" << endl;
}

void CeilingFan::medium()
{
	// turns the ceiling fan on to medium
	speed = MEDIUM;
	cout << location + " ceiling fan is on medium" << endl;
}

void CeilingFan::low()
{
	// turns the ceiling fan on to low
	speed = LOW;
	cout << location + " ceiling fan is on low" << endl;
}

void CeilingFan::off()
{
	// turns the ceiling fan off
	speed = OFF;
	cout << location + " ceiling fan is off" << endl;
}

int CeilingFan::getSpeed()
{
	return speed;
}

// 命令模式中具体的命令类
// LightOnCommand
void LightOnCommand::execute()
{
	light.on();
}

void LightOnCommand::undo()
{
	light.off();
}

// LightOffCommand
void LightOffCommand::execute()
{
	light.off();
}

void LightOffCommand::undo()
{
	light.on();
}

// LivingroomLightOnCommand
void LivingroomLightOnCommand::execute()
{
	light.on();
}

void LivingroomLightOnCommand::undo()
{
	light.off();
}

// LivingroomLightOffCommand
void LivingroomLightOffCommand::execute()
{
	light.off();
}

void LivingroomLightOffCommand::undo()
{
	light.on();
}

// StereoOnCommand
void StereoOnCommand::execute()
{
	stereo.on();
}

void StereoOnCommand::undo()
{
	stereo.off();
}

// StereoOffCommand
void StereoOffCommand::execute()
{
	stereo.off();
}

void StereoOffCommand::undo()
{
	stereo.on();
}

// StereoOnWithCDCommand
void StereoOnWithCDCommand::execute()
{
	stereo.on();
	stereo.setCD();
	stereo.setVolume(11);
}

void StereoOnWithCDCommand::undo()
{
	stereo.off();
}

// CeilingFanHighCommand
void CeilingFanHighCommand::execute()
{
	prevSpeed = ceilingFan.getSpeed();
	ceilingFan.high();
}

void CeilingFanHighCommand::undo()
{
	switch (prevSpeed) 
	{
	case CeilingFan::HIGH: 	 ceilingFan.high(); break;
	case CeilingFan::MEDIUM: ceilingFan.medium(); break;
	case CeilingFan::LOW: 	 ceilingFan.low(); break;
	default: 				 ceilingFan.off(); break;
	}
}

// CeilingFanMediumCommand
void CeilingFanMediumCommand::execute()
{
	prevSpeed = ceilingFan.getSpeed();
	ceilingFan.medium();
}

void CeilingFanMediumCommand::undo()
{
	switch (prevSpeed)
	{
	case CeilingFan::HIGH: 	 ceilingFan.high(); break;
	case CeilingFan::MEDIUM: ceilingFan.medium(); break;
	case CeilingFan::LOW: 	 ceilingFan.low(); break;
	default: 				 ceilingFan.off(); break;
	}
}

// CeilingFanOffCommand
void CeilingFanOffCommand::execute()
{
	prevSpeed = ceilingFan.getSpeed();
	ceilingFan.off();
}

void CeilingFanOffCommand::undo()
{
	switch (prevSpeed)
	{
	case CeilingFan::HIGH: 	 ceilingFan.high(); break;
	case CeilingFan::MEDIUM: ceilingFan.medium(); break;
	case CeilingFan::LOW: 	 ceilingFan.low(); break;
	default: 				 ceilingFan.off(); break;
	}
}

// 这个是命令模式中常用的类:命令宏,用来执行一组命令
void MacroCommand::execute()
{
	for (size_t i = 0; i < commands.size(); i++)
	{
		commands[i]->execute();
	}
}

void MacroCommand::undo()
{
	for (size_t i = 0; i < commands.size(); i++)
	{
		commands[i]->undo();
	}
}

// CommandPatternTest.cpp文件

#include "CommandPattern.h"

void main()
{
	RemoteControl remoteControl;

	Light light("Living Room");
	Stereo stereo("Living Room");
	CeilingFan ceilingFan("Living Room");

	LightOnCommand on1(light);
	LivingroomLightOnCommand on2(light);
	StereoOnCommand on3(stereo);
	CeilingFanHighCommand on4(ceilingFan);
	CeilingFanMediumCommand on5(ceilingFan);

	LightOffCommand off1(light);
	LivingroomLightOffCommand off2(light);
	StereoOffCommand off3(stereo);
	CeilingFanOffCommand off4(ceilingFan);

	vector<Command*> onCommands;
	vector<Command*> offCommands;

	onCommands.push_back(&on1);
	onCommands.push_back(&on2);
	onCommands.push_back(&on3);
	onCommands.push_back(&on4);
	onCommands.push_back(&on5);

	offCommands.push_back(&off1);
	offCommands.push_back(&off2);
	offCommands.push_back(&off3);
	offCommands.push_back(&off4);

	MacroCommand partyOnMacro(onCommands);
	MacroCommand partyOffMacro(offCommands);

	remoteControl.setCommand(0, &partyOnMacro, &partyOffMacro);

	cout << remoteControl.toString() << endl;
	cout << "--- Pushing Macro On---" << endl;
	remoteControl.onButtonWasPushed(0);
	cout << "--- Pushing Macro Off---" << endl;
	remoteControl.offButtonWasPushed(0);
}

    该例的运行结果如图1所示,UML类图如图2所示。

设计模式系列(九)命令模式(Command Pattern)_第1张图片
图1 运行结果

设计模式系列(九)命令模式(Command Pattern)_第2张图片
图2 UML类图

    我们来看一下上述例子中各个类与命令模式中的角色的对应关系:

(1)Command角色:对应于该例中的Command类,由于该例支持撤销操作,所以其中包含了execute()和undo()两个函数,该例中的撤销操作是撤销上一次执行的操作;

(2)ConcreteCommand角色:NoCommand、LightOnCommand等后缀为Command的类都是具体命令角色,其中包含了具体的命令执行者,即Receiver;

(3)Receiver角色:其中的Light类、Stereo类以及CeilingFan类都是接收者,其中每个类都定义了各自的行为,比如on()、off()等函数,而且都是变化的;

(4)Invoker角色:RemoteControl类,其中持有很多命令对象,并有相应的操作来触发执行,注意这个类中有一个setCommand()函数就是用来将Client创建的具体命令对象保存在该类中,用于触发行为;

(5)Client角色:该例中的这个角色就是最后一个源文件,即测试用例,其中就是创建具体的命令及其接收者的程序。

    细心的朋友一定发现其中有一个MacroCommand类,这个类是用来执行一组命令,即所谓的Party模式,这个类中包含一组命令,然后执行时依次执行每个命令,即命令宏,这个是十分常见的应用。

    至于C++的实现程序中需要注意的是typeid的使用,它是RTTI(运行时类型检查)的一种应用,我们主要使用它来判断类型具体的子类类型以及显示。

    总之,命令模式并不是十分复杂,但是却有很多细节需要注意。通常,命令模式也可以用于日志记录与恢复、事务处理与事务回滚等,具体的应用还是需要从实践中慢慢领悟。

你可能感兴趣的:(设计模式,C++,Pattern,command,命令模式,UML类图)