【设计模式】命令模式

命令模式

命令(Command)模式是一种行为型模式,其实现有些烦琐,适用于一些比较专用的场合。本章首先通过一个采用命令模式编写的范例引入命令模式的概念,然后具体阐述命令模式适用的场景,达到让读者对该模式活学活用的目的。在本章的最后,还将阐述命令模式的特点以及一些值得深入思考的话题。

9.1 通过一个范例引出命令模式代码编写方法

设想来到一家饭馆,点两个菜吃------红烧鱼和锅包肉,编写一下这个饭馆的相关代码:

cpp 复制代码
// 厨师类
class Cook {
public:
    // 做红烧鱼
    void cook_fish() {
        cout << "    做一盘红烧鱼菜品" << endl;
    }

    // 做锅包肉
    void cook_meat() {
        cout << "    做一盘锅包肉菜品" << endl;
    }
    // 做其他各种菜品 ... 
};

main 主函数中让厨师做菜:

cpp 复制代码
Cook* pcook = new Cook();
pcook->cook_fish();
pcook->cook_meat();

// 释放资源
delete pcook;

引入服务员类,实现命令模式。首先创建命令对应的抽象父类 Command

cpp 复制代码
// 厨师做的每样菜品对应的抽象类
class Command {
public:
    Command(Cook* pcook) {
        m_pcook = pcook;
    }

    // 作父类时析构函数应该为虚函数
    virtual ~Command() {}
    virtual void Execute() = 0;
protected:
    Cook* m_pcook; // 子类需要访问
};

针对厨师能做的菜实现具体的命令子类:

cpp 复制代码
// 做红烧鱼菜品命令(顾客下的红烧鱼菜品便签)
class CommandFish : public Command {
public:
    CommandFish(Cook* pcook) : Command(pcook) {}
    virtual void Execute() {
        m_pcook->cook_fish();
    }
};

// 做锅包肉菜品命令(顾客下的锅包肉菜品便签)
class CommandMeat : public Command {
public:
    CommandMeat(Cook* pcook) : Command(pcook) {}
    virtual void Execute() {
        m_pcook->cook_meat();
    }
};

main 主函数中使用命令子类:

cpp 复制代码
Cook cook;
Command* pcmd1 = new CommandFish(&cook);
pcmd1->Execute();		// 做红烧鱼

Command* pcmd2 = new CommandMeat(&cook);
pcmd2->Execute();		// 做锅包肉

// 释放资源
delete pcmd1;
delete pcmd2;

引入服务员类 Waiter

cpp 复制代码
// 服务员类
class Waiter {
public:
    void SetCommand(Command* pcommand) {
        m_pcommand = pcommand;
    }
    void Notify() {
        m_pcommand->Execute();
    }
private:
    Command* m_pcommand;
};

main 主函数中使用服务员类:

cpp 复制代码
Cook cook;
Waiter* pwaiter = new Waiter();

Command* pcmd1 = new CommandFish(&cook);
pwaiter->SetCommand(pcmd1);
pwaiter->Notify();	// 做红烧鱼

Command* pcmd2 = new CommandMeat(&cook);
pwaiter->SetCommand(pcmd2);
pwaiter->Notify();	// 做锅包肉

// 释放资源
delete pcmd1;
delete pcmd2;
delete pwaiter;

修改服务员类以支持多道菜品:

cpp 复制代码
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:
    std::list<Command*> m_commlist; // 菜品列表
};

main 主函数中使用修改后的服务员类:

cpp 复制代码
Cook cook;
Command* pcmd1 = new CommandFish(&cook);
Command* pcmd2 = new CommandMeat(&cook);

Waiter* pwaiter = new Waiter();
pwaiter->AddCommand(pcmd1);
pwaiter->AddCommand(pcmd2);

pwaiter->Notify(); // 服务员一次性通知厨师做多道菜

// 释放资源
delete pcmd1;
delete pcmd2;
delete pwaiter;

9.2 引入命令模式

命令模式的 UML 图包含 5 种角色:

(1) Receiver(接收者类) :如 Cook 类,提供业务处理接口。

(2) Invoker(调用者类) :如 Waiter 类,通过命令对象执行请求。

(3) Command(抽象命令类) :如 Command 类,声明执行操作的接口。

(4) ConcreteCommand(具体命令类) :如 CommandFishCommandMeat 类,实现执行请求的方法。

(5) Client(客户端):创建命令对象,设定接收者,驱动调用者执行动作。

命令模式的实现意图:将请求封装为对象,以便通过参数传递,支持排队执行、日志记录、可撤销操作等。

9.3 命令模式用途研究

9.3.1 改造范例增加对象使用时的独立性

调整 Command 类析构函数:

cpp 复制代码
virtual ~Command() {
    if (m_pcook != nullptr) {
        delete m_pcook;
        m_pcook = nullptr;
    }
}

引入实习服务员类 Traineewaiter

cpp 复制代码
// 实习服务员类
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;
};

main 主函数中使用实习服务员类:

cpp 复制代码
Traineewaiter* pwaitersx1 = new Traineewaiter(new CommandFish(new Cook()));
pwaitersx1->Notify(); // 做红烧鱼
Traineewaiter* pwaitersx2 = new Traineewaiter(new CommandMeat(new Cook()));
pwaitersx2->Notify(); // 做锅包肉

// 释放资源
delete pwaitersx1;
delete pwaitersx2;

使用 聚合命令集合 继承 继承 关联 关联 <<Client>> Client Waiter +AddCommand() +DelCommand() +Notify() Command +Execute() Cook +cook_fish() +cook_meat() CommandFish #m_pcook: Cook +Execute() CommandMeat #m_pcook: Cook +Execute()

9.3.2 命令模式使用场景与特点总结

适用场景:
  1. 绘图软件:实现撤销、重做、日志记录等功能。
  2. 遥控器控制:解耦遥控器与家用电器,灵活控制。
  3. 任务调度:实现任务的定期调度执行。
  4. 游戏系统:实现时光倒流、情景回放等功能。
特点:
  • 解耦:请求发送者和接收者解耦。
  • 开闭原则 :通过增加 Command 子类支持功能扩充。
  • 子类较多 :实现时可能引入较多 Command 子类。
相关思考:
  • 命令对象与回调函数:命令模式是回调机制的面向对象替代品。
  • 极端情形:允许不引入调用者类或接收者类,确保命令对象找到接收者即可。
  • 与可调用对象比较:可调用对象性能更高,与模板结合更灵活,合适场合优先使用可调用对象。
相关推荐
Hanson Huang11 分钟前
23种设计模式-享元(Flyweight)设计模式
java·设计模式·享元模式·结构型设计模式
LoveXming1 小时前
Qt 多线程的两种实现方式
开发语言·qt·命令模式
Aphelios3803 小时前
Java全栈面试宝典:内存模型与Spring设计模式深度解析
java·学习·spring·设计模式·云原生·面试
NorthCastle4 小时前
设计模式-结构型模式-外观模式
java·设计模式·外观模式
刀法如飞4 小时前
MVC与MVP/MVVM/DDD架构对比,不同语言实现
设计模式·架构·mvc
hweiyu006 小时前
【Java全栈进阶架构师实战:从设计模式到SpringCloudAlibaba,打造高可用系统】
java·spring boot·分布式·spring·spring cloud·设计模式
系统工程实验室9 小时前
系统架构设计-防腐层、门面模式和适配器模式
java·设计模式·适配器模式
minaMoonGirl10 小时前
软件设计模式-第一章
设计模式
前端花园12 小时前
看完你还能记住几个设计模式?
前端·设计模式
渊渟岳12 小时前
掌握设计模式--备忘录模式
设计模式