一 定义
命令模式(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;
}
运行结果: