命令模式,餐厅订单管理系统C++

发布于:2025-09-12 ⋅ 阅读:(21) ⋅ 点赞:(0)

一、为什么用命令模式

    在软件设计中,命令模式(Command Pattern)提供了一种将请求封装为对象的方式,从而使请求的发送者和执行者解耦。这在以下场景非常有用:当操作需要支持撤销、排队、延迟执行或日志记录时,命令模式能将逻辑统一管理。

    餐厅点餐系统是典型场景:服务员记录顾客订单,厨师按顺序制作,每道菜可独立撤销或取消,但正在制作的菜不能撤销。直接在服务员或厨师中处理复杂逻辑,会导致代码耦合,难以扩展。使用命令模式,将“下单”“做菜”“撤销”操作封装成独立命令对象,既清晰又易维护。

二、场景说明

    在这个场景中,餐厅的服务员负责接收顾客点餐。每道菜都是一个独立的命令对象,例如“红烧鱼”和“家乡煎豆腐”。顾客下单后,服务员将订单加入队列,但厨师是按顺序做菜的,当前只做一道菜,做完后才开始下一道。

    系统支持顾客在下单后撤销某些菜,但有严格限制:正在烹饪的菜不可撤销,只允许撤销尚未开始的菜。服务员可以继续接收新订单,撤销旧订单,厨师按照队列顺序烹饪所有菜品。这样既满足实际业务需求,也充分体现了命令模式的灵活性和可扩展性。

三、类图

四、C++代码实现

#include <iostream>
#include <memory>
#include <map>
#include <deque>
#include <algorithm>

enum class OrderStatus { Pending, Cooking, Done };

// 命令接口
class Command {
public:
    virtual ~Command() = default;
    virtual void execute() = 0;  
    virtual void undo() = 0;     
};

// 厨师
class Chef {
public:
    void cookFish() { std::cout << "厨师:开始做红烧鱼!\n"; }
    void cancelFish() { std::cout << "厨师:取消红烧鱼。\n"; }

    void cookTofu() { std::cout << "厨师:开始做家乡煎豆腐!\n"; }
    void cancelTofu() { std::cout << "厨师:取消家乡煎豆腐。\n"; }
};

// 具体命令
class FishCommand : public Command {
public:
    FishCommand(Chef* chef) : chef_(chef) {}
    void execute() override { chef_->cookFish(); }
    void undo() override { chef_->cancelFish(); }
private:
    Chef* chef_;
};

class TofuCommand : public Command {
public:
    TofuCommand(Chef* chef) : chef_(chef) {}
    void execute() override { chef_->cookTofu(); }
    void undo() override { chef_->cancelTofu(); }
private:
    Chef* chef_;
};

// 服务员
class Waiter {
public:
    int takeOrder(std::unique_ptr<Command> cmd) {
        int id = nextId_++;
        orders_[id] = { std::move(cmd), OrderStatus::Pending };
        orderQueue_.push_back(id);
        std::cout << "服务员:记录订单编号 " << id << "\n";
        cookNextIfIdle();
        return id;
    }

    void cancelOrder(int id) {
        auto it = orders_.find(id);
        if (it != orders_.end()) {
            if (it->second.status == OrderStatus::Pending) {
                it->second.command->undo();
                orderQueue_.erase(
                    std::remove(orderQueue_.begin(), orderQueue_.end(), id),
                    orderQueue_.end()
                );
                orders_.erase(it);
            } else {
                std::cout << "订单 " << id << " 正在做或已完成,无法撤销。\n";
            }
        } else {
            std::cout << "没有找到编号为 " << id << " 的订单。\n";
        }
    }

private:
    struct OrderItem {
        std::unique_ptr<Command> command;
        OrderStatus status;
    };

    void cookNextIfIdle() {
        if (isCooking_ || orderQueue_.empty()) return;
        isCooking_ = true;
        while (!orderQueue_.empty()) {
            int id = orderQueue_.front();
            orderQueue_.pop_front();
            auto it = orders_.find(id);
            if (it != orders_.end()) {
                it->second.status = OrderStatus::Cooking;
                it->second.command->execute();
                it->second.status = OrderStatus::Done;
                orders_.erase(it); 
            }
        }
        isCooking_ = false;
        std::cout << "厨师:所有订单已完成。\n";
    }

    int nextId_ = 1;
    bool isCooking_ = false;
    std::map<int, OrderItem> orders_;
    std::deque<int> orderQueue_;
};

// 测试
int main() {
    Chef chef;
    Waiter waiter;

    int o1 = waiter.takeOrder(std::make_unique<FishCommand>(&chef));
    int o2 = waiter.takeOrder(std::make_unique<TofuCommand>(&chef));
    int o3 = waiter.takeOrder(std::make_unique<FishCommand>(&chef));

    waiter.cancelOrder(o2); // 可以取消豆腐,只要它还没开始做
    int o4 = waiter.takeOrder(std::make_unique<TofuCommand>(&chef)); // 新下单豆腐

    return 0;
}

五、总结

    通过这个餐厅点餐示例,我们可以看到命令模式在实际开发中的几个优势:

  1. 解耦请求和执行:服务员只负责接单和管理订单,厨师专注于烹饪逻辑,二者职责清晰分明。

  2. 支持撤销与延迟操作:每道菜都是独立的命令对象,未开始的菜可以随时取消,而正在烹饪的菜则安全执行。

  3. 扩展性强:增加新菜品只需创建新的命令类,无需修改服务员或厨师核心逻辑,符合开闭原则。

  4. 维护方便:通过订单状态管理和队列处理,烹饪顺序和撤销逻辑清晰可控。

这个示例不仅清楚地展示了命令模式的用法,也贴合实际餐厅点餐流程,对于想在 C++ 项目中灵活运用设计模式的开发者,很有参考意义。


网站公告

今日签到

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