命令模式:
将请求封装成对象,以便使用不同的请求、日志、队列等来参数化其他对象。命令模式也支持撤销操作。
命令模式的角色:
- 传递命令对象(Invoker):是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。
- 抽象命令接口(Command):声明执行命令的接口,拥有执行命令的抽象方法execute()。
- 具体的命令对象(ConcreteCommand):是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。
- 接受者对象(Receiver):执行命令功能的相关操作,是具体命令对象业务的真正实现者。
- 客户端对象(Client):创建具体命令的对象并且设置命令对象的接受者。
优点:
- 降低系统的耦合度。命令模式能将调用操作的对象与实现该操作的对象解耦。
- 增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,它满足OCP原则,对扩展比较灵活。
- 可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
- 方便实现 Undo 和 Redo 操作。命令模式可以与备忘录模式结合,实现命令的撤销与恢复。
缺点:
可能产生大量的具体命令类。
适用环境:
- 使用命令模式作为“CallBack”在面向对象系统中的替代。“CallBack”讲的便是先将一个函数登记上,然后在以后调用此函数。
- 需要在不同的时间指定请求、将请求排队。一个命令对象和原先的请求发出者可以有不同的生命期。换言之,原先的请求发出者可能已经不在了,而命令对象本身仍然是活动的。这时命令的接收者可以是在本地,也可以在网络的另外一个地址。命令对象可以在串形化之后传送到另外一台机器上去。
- 系统需要支持命令的撤消(undo)。命令对象可以把状态存储起来,等到客户端需要撤销命令所产生的效果时,可以调用undo()方法,把命令所产生的效果撤销掉。命令对象还可以提供redo()方法,以供客户端在需要时,再重新实施命令效果。
- 如果一个系统要将系统中所有的数据更新到日志里,以便在系统崩溃时,可以根据日志里读回所有的数据更新命令,重新调用Execute()方法一条一条执行这些命令,从而恢复系统在崩溃前所做的数据更新。
区别:
命令模式与策略模式的区别:
- 命令模式与策略模式都封装了变化,但命令模式封装的是请求的变化,而策略模式封装的是算法的变化。
- 命令模式可以抽象化成策略模式。策略模式较简单,而命令模式比较复杂。策略模式聚焦的是对相同请求更换解决方案的灵活性;而命令模式聚焦的是对多请求变化的封装以及对相同请求不同的请求形式解决方法的可复用性。
eg:
class Barbecuer
{
public:
void BakeMutton(){cout<<"Bake mutton"<<endl;}
void BakeChickenWing(){cout<<"Bake ChickenWing"<<endl;}
};
//----------------------------------------------
/*抽象命令类:是执行具体操作的接口*/
class Command
{
public:
Command(){}
Command(Barbecuer *receiver):p_receiver(receiver){}
virtual void ExecuteCommand() = 0; //执行命令
protected:
Barbecuer *p_receiver;
};
/*具体命令类:烤羊肉串命令*/
class BakeMuttonCommand:public Command
{
public:
BakeMuttonCommand(Barbecuer *receiver){p_receiver = receiver;}
void ExecuteCommand(){p_receiver->BakeMutton();}
};
/*具体命令类:烤鸡翅串命令*/
class BakeChickenWingCommand:public Command
{
public:
BakeChickenWingCommand(Barbecuer *receiver){p_receiver = receiver;}
void ExecuteCommand()
{p_receiver->BakeChickenWing();}
};
//----------------------------------------------
/*服务员类*/
class Waiter
{
public:
void SetOrder(Command *command)
{
p_commandList.push_back(command);
cout << "增加烤肉命令" << endl;
}
void Notify()
{
vector<Command*>::iterator i;
for(i = p_commandList.begin(); i != p_commandList.end(); ++ i)
(*i)->ExecuteCommand();
}
private:
vector<Command *>p_commandList; //这里相当于一个命令对象队列
};
//----------------------------------------------
//use
Barbecuer *p_cook = new Barbecuer();
Command *p_mutton = new BakeMuttonCommand(p_cook);
Command *p_chickenwing = new BakeChickenWingCommand(p_cook);
Waiter *p_waiter = new Waiter();
//将订单对象推送到命令队列
p_waiter->SetOrder(p_mutton);
p_waiter->SetOrder(p_chickenwing);
//服务员通知烤肉师傅具体订单
p_waiter->Notify();