文章目录

  • ​​1. 基础知识​​
  • ​​2. 逻辑代码​​
  • ​​3. 应用​​
  • ​​3.1 服务员命令厨师上菜​​

1. 基础知识

命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
命令模式,将一个请求封装成一个对象, 从而使你可用不同的请求对客户进行参数化, 对请求排队或记录请求日志, 以及支持可撤销的操作

意图:
将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。

主要解决:
在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。

何时使用:
在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。

如何解决:
通过调用者调用接受者执行命令,顺序:调用者→命令→接受者。

关键代码:
定义三个角色:1、received 真正的命令执行对象 2、Command 3、invoker 使用命令对象的入口

优点:
1、降低了系统耦合度。 2、新的命令可以很容易添加到系统中去。

缺点:
使用命令模式可能会导致某些系统有过多的具体命令类。

使用场景:

认为是命令的地方都可以使用命令模式,比如:
1、GUI 中每一个按钮都是一条命令。
2、模拟 CMD。
注意事项:
系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作,也可以考虑使用命令模式,见命令模式的扩展。

2. 逻辑代码

C++设计模式 命令模式(服务员命令厨师)_成员变量


松耦合设计

using namespace std;

// 命令接收这或者命令执行者
class Receiver{
public:
Receiver(string s):m_name(s){
cout<< "来了个: " << m_name << endl;
}
void Action(){
cout<< "收到命令, 并且开始写个圣旨" << endl;
}
private:
string m_name;
};

// 抽象命令, 包含命令执行者
class Command{
protected:
Receiver *receiver = nullptr;
public:
Command(Receiver *receiver): receiver(receiver){}
virtual void Execute() = 0;
virtual ~Command(){}
};

// 具体命令调用的是命名执行者定义的
class ConcreteCommand : public Command{
public:
ConcreteCommand(Receiver *receiver) : Command(receiver){}

void Execute(){
receiver->Action();
}
};

// 下命令者
class Invoker{
private:
// 这个如果是单个对象, 就只能是一道命令一道命令的下, 如果是一个队列, 就可以把多个命令加载到一起, 然后统一下命令
Command *command = nullptr;
string m_name;
public:
Invoker(string s) : m_name(s) {
cout << "新来了个" << s << endl;
}
void SetCommand(Command *command){
this->command = command;
}

void ExecuteCommand(){
command->Execute();
}
};

// client客户端
void client(){
Receiver *r = new Receiver("太监");
Command *c = new ConcreteCommand(r); // 这是一道圣旨, 只能有太监下
Invoker *i = new Invoker("皇上");

i->SetCommand(c); // 皇上知道了能下这道圣旨
i->ExecuteCommand(); // 皇上说了这道圣旨给太监下

delete r;
delete c;
delete i;
}

int main(int argc, char const *argv[]){
client();
return 0;
}
/*
来了个: 太监
新来了个皇上
收到命令, 并且开始写个圣旨
*/

3. 应用

3.1 服务员命令厨师上菜

C++设计模式 命令模式(服务员命令厨师)_成员变量_02

代码逻辑:

  1. 创建烤串师傅类
  2. 创建命令抽象类 包含烤串师傅对象
  3. 实现命令类, 比如烤羊肉的命令类和烤鸡翅的命令类
  4. 创建服务员类, 包含列表成员变量保存增加的命令

包含功能:
5. 用户一次性点完, 然后服务员通知厨房去做
6. 判断食物是否还有, 没有直接通知
7. 客户点了哪些东西, 进行日志记录
8. 有些肉太多, 客户需要取消掉

#include <iostream>
#include <vector>
#include <string>
#include <list>
using namespace std;

// 烤串师傅
class Barbecuer{
public:
void BakeMutton(){
cout<< "烤羊肉" << endl;
}

void BakeChickenWing(){
cout << "烤鸡翅" << endl;
}
};

// 抽象烤串师傅, 确定烤肉者
class Command{
protected:
Barbecuer *receiver = nullptr; // 生产者
int capacity; // 能生产的数量

public:
Command(Barbecuer *r, int c): receiver(r), capacity(c){}
virtual ~Command(){
// 因为receiver不是在这里面new的, 所以尽量不要在这里面删除
// if(receiver != nullptr){
// cout<< " new 对象 receiver已经被删除" << endl;
// delete receiver; receiver = nullptr;
// }
}
virtual void ExcuteCommand() = 0;
virtual bool isHave(){ // 判断有无剩余
return this->capacity > 0;
}
virtual bool addCapacity(int k){ // 容量加k
cout << "当前有" << capacity << "卖掉" << k << endl;
capacity += k;
if(capacity < 0){
capacity -= k; // 在加回来, 返回一个数量
cout<< "容量减超, 当前还剩"<< capacity << endl;
return false;
}else{
return true;
}
}
};

// 具体命令类
// 烤羊肉串
class BakeMuttonCommand : public Command{
public:
BakeMuttonCommand(Barbecuer *receiver, int capacity) : Command(receiver, capacity){
cout << "请了师傅, 并且数量有" << capacity << endl;
}
void ExcuteCommand(){
receiver->BakeMutton(); // 这里直接调用继承的父类的成员变量
}
};

// 烤鸡翅命令
class BakeChickenWingCommand : public Command{
public:
BakeChickenWingCommand(Barbecuer *receiver, int capacity) : Command(receiver, capacity){
cout << "请了师傅, 并且数量有" << capacity << endl;
}
void ExcuteCommand(){
receiver->BakeChickenWing(); // 这里直接调用继承的父类的成员变量
}
};

// 服务员
class Waiter{
private:
Command *command = nullptr;
public:
// 设置订单
void SetOrder(Command *command) {
if(command->isHave()){
command->addCapacity(-1);
this->command = command;
}else{
cout<< "没肉了" << endl;
}
}
// 通知执行
void Notify(){
this->command->ExcuteCommand();
}
};

// 牛叉服务员
/*
1. 客户点完烧烤后, 服务员一次性通知师傅开始制作
2. 如果此时鸡翅没了, 不应该客户判断是否还有, 应该是服务员或者师傅决定请求
3. 客户到底点了哪些烧烤或者饮料, 需要有记录日志, 以备收费和后期统计
4. 客户如果点的过多, 可以选择取消哪些没有烤的肉串
*/
class WaiterLeader{
private:
// 存放具体命令的容器
list<Command*> orders; // 菜单

public:
void SetOrder(Command *command){
if(command->isHave()){ // 有肉
command->addCapacity(-1);
orders.push_back(command);
cout << "增加订单xx" << "总价xx" << endl;
// 最后总价按照orders中进行遍历计算即可
}else{
cout << "莫得了" << endl;
}
}

void CancelOrder(Command *command){
orders.remove(command);
command->addCapacity(1);
cout << "取消订单 xx , 取消时间 xx" << endl;
}

void Notify(){
for(auto &a : orders){
a->ExcuteCommand();
}
}
};

void test1(){
Barbecuer *boy = new Barbecuer(); // 厨师
Command *bakeMuttonCommand1 = new BakeMuttonCommand(boy, 2); // 这个厨师对应的菜单
Command *BakeChickenWingCommand1 = new BakeChickenWingCommand(boy, 4); // 这个厨师对应的菜单
Waiter *girl = new Waiter();

// 开门营业
girl->SetOrder(bakeMuttonCommand1); // 服务员给厨师下单
girl->SetOrder(bakeMuttonCommand1); // 服务员给厨师下单
girl->SetOrder(bakeMuttonCommand1); // 服务员给厨师下单
girl->Notify(); // 通知到货

girl->SetOrder(BakeChickenWingCommand1); // 服务员给厨师下单
girl->SetOrder(BakeChickenWingCommand1); // 服务员给厨师下单

if(boy != nullptr) delete boy;
delete bakeMuttonCommand1;
delete girl;
delete BakeChickenWingCommand1;
}

int main(int argc, char const *argv[]){
Barbecuer *boy = new Barbecuer(); // 先搞一个厨师过来
Command *chickenWing = new BakeChickenWingCommand(boy, 10); // 一道烧鸡送给各位, 烧鸡总共有10只
Command *mutton = new BakeMuttonCommand(boy, 4); // 一道烤全羊, 送给各位, 烤羊总共4只
WaiterLeader *girl = new WaiterLeader();

// 开门营业
girl->SetOrder(chickenWing); // 服务员收到一个鸡翅要求
girl->SetOrder(chickenWing); // 服务员收到另一只要求
girl->SetOrder(mutton); // 服务员收到一直烤全羊的要求

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

delete boy;
delete chickenWing;
delete mutton;
delete girl;
return 0;
}