命令模式的场景
现在需要设计这样的场景:厨师类负责做菜,比如红烧鱼、锅包肉等菜,而顾客一般不能直接命令厨师做哪些菜,而是需要写一个便签,写上需要做哪些菜,然后交给厨师来做。
#include <iostream>
#include <vector>
#include <list>
using namespace std;
namespace hjl_project1
{
class Cook
{
public:
void cook_fish()
{
cout<<"做一盘红烧鱼"<<endl;
}
void cook_meat()
{
cout<<"做一盘锅包肉"<<endl;
}
//做其他菜品,略
};
//厨师做的每样菜品对应的抽象类
class Command
{
public:
Command(Cook*pcook)
{
m_pcook=pcook;
}
//做父类时析构函数为虚函数
virtual ~Command()
{
if(m_pcook!=nullptr)
{
delete m_pcook;
m_pcook=nullptr;
}
}
//通知厨师做菜
virtual void Execute()=0;
protected:
//命令对应的厨师
Cook*m_pcook;
};
//做红烧鱼菜品的命令子类
class CommandFish:public Command
{
public:
CommandFish(Cook*pcook)
:Command(pcook)
{}
virtual void Execute()
{
m_pcook->cook_fish();
}
};
//做锅包肉菜品的命令子类
class CommandMeate:public Command
{
public:
CommandMeate(Cook*pcook)
:Command(pcook)
{}
virtual void Execute()
{
m_pcook->cook_meat();
}
};
};
int main()
{
using namespace hjl_project1;
//创建做鱼的便签,交给厨师
Command* pcmd1=new CommandFish(new Cook());
pcmd1->Execute();
//创建做肉的便签,交给厨师
Command* pcmd2=new CommandMeate(new Cook());
pcmd2->Execute();
//释放资源 delete ...
}
但是上面这样做有一些不方便,那就是顾客每次都要创建一个便签,然后调用Execute
来通知厨师。
这时候我们引入一个服务员类,这个服务员类接收所有的便签,然后替顾客通知厨师。
#include <iostream>
#include <vector>
#include <list>
using namespace std;
namespace hjl_project1
{
class Cook
{
public:
void cook_fish()
{
cout<<"做一盘红烧鱼"<<endl;
}
void cook_meat()
{
cout<<"做一盘锅包肉"<<endl;
}
//做其他菜品,略
};
//初识做的每样菜品对应的抽象类
class Command
{
public:
Command(Cook*pcook)
{
m_pcook=pcook;
}
//做父类时析构函数为虚函数
virtual ~Command()
{
if(m_pcook!=nullptr)
{
delete m_pcook;
m_pcook=nullptr;
}
}
virtual void Execute()=0;
protected:
Cook*m_pcook;
};
//做红烧鱼菜品的命令子类
class CommandFish:public Command
{
public:
CommandFish(Cook*pcook)
:Command(pcook)
{}
virtual void Execute()
{
m_pcook->cook_fish();
}
};
//做锅包肉菜品的命令子类
class CommandMeate:public Command
{
public:
CommandMeate(Cook*pcook)
:Command(pcook)
{}
virtual void Execute()
{
m_pcook->cook_meat();
}
};
//------服务员类
class Waiter
{
public:
//顾客将便签交给服务员
void AddCommand(Command*pcommand)
{
m_commlist.push_back(pcommand);
}
//如果顾客想撤单,则将便签从列表中删除
void DelCommand(Command*pcommand)
{
m_commlist.remove(pcommand);
}
void Notify()
{
//服务员将便签交到厨师手里让厨师做菜
for(auto iter=m_commlist.begin();iter!=m_commlist.end();++iter)
{
(*iter)->Execute();
}
}
private:
list<Command*>m_commlist;
};
//实习服务员类,每次只能通知一个command
class Traineewaiter
{
public:
Traineewaiter(Command*pcommand):m_pcommand(pcommand){}
void Notify()
{
m_pcommand->Execute();
}
~Traineewaiter()
{
if(m_pcommand!=nullptr)
{
delete m_pcommand;
m_pcommand=nullptr;
}
}
private:
Command* m_pcommand;
};
};
int main()
{
using namespace hjl_project1;
Command* pcmd1=new CommandFish(new Cook());
Command* pcmd2=new CommandMeate(new Cook());
Waiter* pwaiter1=new Waiter();
pwaiter1->AddCommand(pcmd1);
pwaiter1->AddCommand(pcmd2);
//服务员一次性通知厨师
pwaiter1->Notify();
//释放资源 delete ...
}
命令模式的定义
命令模式一共有四种角色:
- 接收者类:上面的Cook类,他执行具体的动作,比如cook_fish等
- 调用者类:waiter类,他只与command类存在关联关系。
- Command(抽象命令类):声明了Execute用来调用接收者类的具体接口。
- 具体命令类:继承Command,实现了Execute方法,调用接收者的具体接口,比如cook_fish。
定义:将一个请求或者命令封装为一个对象,以便这些请求可以以对象的方式通过参数进行传递,对象化的请求还可以排队执行或者根据需要将这些请求录入日志进行查看,或者对这些请求进行撤销。
从上面的定义不难看出,命令模式执行的命令具有 异步执行、延迟执行、排队执行、撤销命令这些特点,所以它适用的场景有:
- 绘图软件需要进行撤销操作,因为保存了命令的list,所以可以给每个具体命令类增加一个撤销接口,需要撤销时执行即可。
- 任务的定期调度执行。我们可以指定调用者类的执行时间。
- 游戏中的时光倒流系统或者回放系统的实现。将玩家的操作以具体命令类的方式记录到命令流中,就可以回放的功能。
命令模式的特点:
- 调用者和接收者解耦,调用者和接收者之间可以是多对多的关系。
- 增加新功能的时候,对接收者类的修改是不可避免的,但是可以通过增加新的具体命令类,从而不用修改调用者。
- 具体的命令类可能会过多,这是命令模式的缺点。