14行为型设计模式——命令模式

一、命令模式简介

命令模式(Command Pattern)是一种行为型设计模式,它将请求封装成一个对象,从而使你可以用不同的请求对客户端进行参数化,支持请求的排队和日志记录,以及支持可撤销的操作。命令模式是最常见的方式之一。例如前面几十个设计模式提供的设计方法中a.out本身就是一个命令(可执行程序),当我们去执行a.out可以产生不同的行为或状态。

命令模式的结构图

命令模式的主要组成部分

  1. 命令接口(Command Interface): 定义了一个抽象的执行方法(通常是 execute() 方法)。

  2. 具体命令(Concrete Command): 实现了命令接口,定义了一个接收者对象并调用接收者的相关操作。

  3. 接收者(Receiver): 负责实际处理请求的对象,执行具体的操作。

  4. 调用者(Invoker): 持有一个命令对象,并在适当的时候调用命令对象的 execute() 方法。

  5. 客户端(Client): 创建具体命令对象,并设置接收者,然后将命令对象传递给调用者。 

命令模式的应用场景 

1. UI操作
  • 撤销/重做功能:在图形用户界面(GUI)中,命令模式可以用于实现撤销和重做功能。例如,每个用户操作(如文本编辑、格式化、图形绘制)都可以被封装成一个命令对象,当用户选择撤销时,系统可以执行之前的操作并保存当前状态以备重做。
  • 按钮操作:在 GUI 中,按钮可以绑定不同的命令。例如,一个“保存”按钮可能绑定到“保存文件”命令,另一个“打印”按钮绑定到“打印文档”命令。
2. 任务调度
  • 任务排队和日志记录:在任务调度系统中,命令模式允许将任务封装成命令对象,并将这些任务排队执行或记录。这样可以实现任务的调度、日志记录和撤销操作。
  • 定时任务:在定时任务系统中,可以将不同的定时任务封装成命令对象,并在指定的时间执行这些任务。
3. 交易处理
  • 银行交易:在金融系统中,银行交易(如存款、取款、转账)可以被封装成命令对象,方便对交易进行管理、日志记录和撤销。
4. 操作系统命令
  • 操作系统的命令行工具:许多操作系统提供命令行工具来执行各种操作,这些工具内部也使用命令模式来处理不同的操作。例如,Unix/Linux 中的grep、sed、awk三剑客等命令。
5. 宏命令
  • 复杂操作:在需要执行一系列操作时,可以将这些操作封装成一个宏命令对象。例如,自动化脚本可以使用命令模式来执行一系列操作步骤,如备份文件、清理日志和发送通知。
6. 游戏开发
  • 游戏操作:在游戏开发中,玩家的操作(如移动角色、攻击敌人)可以被封装成命令对象,这样可以支持复杂的操作序列、操作记录和撤销。
7. 远程控制
  • 设备控制:在远程控制系统中(如智能家居系统),可以通过命令模式封装对各种设备的控制操作。例如,打开/关闭灯、调节温度、播放音乐等操作都可以封装成不同的命令对象,并由遥控器发出。
8. 网络请求
  • 异步请求:在网络编程中,命令模式可以用来封装网络请求,支持异步执行和回调。例如,将网络请求封装成命令对象,可以实现请求的排队、重试和取消操作

 二、命令模式的设计方法

设计命令模式模拟遥控器开关灯的行为

command.cpp

#include <iostream>
#include <vector>
#include <memory>
#include <string>

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

// 接收者
class Light {
public:
    void turnOn() {
        std::cout << "The light is on" << std::endl;
    }
    void turnOff() {
        std::cout << "The light is off" << std::endl;
    }
};

// 具体命令:开灯
class LightOnCommand : public Command {
public:
    LightOnCommand(Light& light) : light_(light) {}
    
    void execute() override {
        light_.turnOn();
    }

    void undo() override {
        light_.turnOff();
    }
    
private:
    Light& light_;
};

// 具体命令:关灯
class LightOffCommand : public Command {
public:
    LightOffCommand(Light& light) : light_(light) {}
    
    void execute() override {
        light_.turnOff();
    }

    void undo() override {
        light_.turnOn();
    }
    
private:
    Light& light_;
};

// 调用者
class RemoteControl {
public:
    void setCommand(std::shared_ptr<Command> command) {
        command_ = command;
    }

    void pressButton() {
        if (command_) {
            command_->execute();
            commandHistory.push_back(command_);
            history.push_back(getCommandName(command_) + " 被执行");
        }
    }

    void pressUndo() {
        if (!commandHistory.empty()) {
            auto lastCommand = commandHistory.back();
            lastCommand->undo();
            commandHistory.pop_back();
            history.push_back(getCommandName(lastCommand) + " 被撤销");
        }
    }

    void printHistory() const {
        for (const auto& entry : history) {
            std::cout << entry << std::endl;
        }
    }
    
private:
    std::shared_ptr<Command> command_;
    std::vector<std::shared_ptr<Command>> commandHistory;
    std::vector<std::string> history;

    std::string getCommandName(const std::shared_ptr<Command>& command) const {
        if (std::dynamic_pointer_cast<LightOnCommand>(command)) {
            return "开灯";
        } else if (std::dynamic_pointer_cast<LightOffCommand>(command)) {
            return "关灯";
        }
        return "无效";
    }
};

// 模拟客户端行为
void doWorking() {
    Light livingRoomLight;

    auto lightOn = std::make_shared<LightOnCommand>(livingRoomLight);
    auto lightOff = std::make_shared<LightOffCommand>(livingRoomLight);

    RemoteControl remote;

    // 按下开灯键
    remote.setCommand(lightOn);
    remote.pressButton();
    
    // 又按下开灯键
    remote.setCommand(lightOn);
    remote.pressButton();
    
    // 又又按下开灯键
    remote.setCommand(lightOn);
    remote.pressButton();
    
    // 关灯
    remote.setCommand(lightOff);
    remote.pressButton();

    // 撤销最后一个命令
    remote.pressUndo();

    // 输出历史记录
    remote.printHistory();
}

int main() {
    //start working
    doWorking();
    return 0;
}

运行效果

 

三、总结

命令模式是一个十分实用的行为型设计模式,将复杂的内部实现(请求)全部封装,对外显示一个命令形态,对客户端来说十分友好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值