【设计模式】命令模式

发布于:2025-03-25 ⋅ 阅读:(54) ⋅ 点赞:(0)

命令模式

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

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

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

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

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

main 主函数中让厨师做菜:

Cook* pcook = new Cook();
pcook->cook_fish();
pcook->cook_meat();

// 释放资源
delete pcook;

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

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

    // 作父类时析构函数应该为虚函数
    virtual ~Command() {}
    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 CommandMeat : public Command {
public:
    CommandMeat(Cook* pcook) : Command(pcook) {}
    virtual void Execute() {
        m_pcook->cook_meat();
    }
};

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

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

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

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

引入服务员类 Waiter

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

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

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;

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

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 主函数中使用修改后的服务员类:

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 类析构函数:

virtual ~Command() {
    if (m_pcook != nullptr) {
        delete m_pcook;
        m_pcook = nullptr;
    }
}

引入实习服务员类 Traineewaiter

// 实习服务员类
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 主函数中使用实习服务员类:

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 子类。
相关思考:
  • 命令对象与回调函数:命令模式是回调机制的面向对象替代品。
  • 极端情形:允许不引入调用者类或接收者类,确保命令对象找到接收者即可。
  • 与可调用对象比较:可调用对象性能更高,与模板结合更灵活,合适场合优先使用可调用对象。

网站公告

今日签到

点亮在社区的每一天
去签到