精读C++20设计模式——行为型设计模式:命令模式

精读C++20设计模式------行为型设计模式:命令模式

前言:Lets Command!

​ Command设计模式实际上不太Command。这个比较反直觉。因为Command设计模式压根就不是直接死命令对象到底怎么做事情。而是发送命令,接收对象根据发送者发送的命令执行代码。

命令模式干什么

​ 命令模式将我们对API的操作封装成命令,命令的调用就是对对象的操作。看起来好像没什么问题,比如说:

cpp 复制代码
class BankAccount {
	int balance = 0;
	constexpr int overdraft_limit = -500;
public:
	void deposit(int amount) { balance += amount; }
	void withdraw(int amount) { 
		if(balance - amount < overdraft_limit) 
			return;
         blance -= amount;
	}
};

​ 我们把类完成了!现在我们可以这样做了。

cpp 复制代码
struct Command { virtual void call() const = 0; };

struct BankAccountCmd : Command
{
	BankAccount& ba;
    enum class AcceptableAction { deposit, withdraw } action;
    int amount;
    
    // BankAccountCmd init omitted
    
    void call() const override {
      	switch(action){
            case deposit:
                // process all the deposit relative
            break;
            case withdraw:
                // process all the withdraw relative
            break;
        }  
    };
};

​ 你看到了嘛?我们现在立马就可以无任何侵入的做比直接调用显然更多的事情了:

cpp 复制代码
BankAccount ba;
BankAccountCmd bacmd {ba, AcceptableAction::deposit, 500};
bacmd.call();

​ 完事。你发现我们完全可以对Command做额外的提交约束啊等一系列的事情,完全不用动BankAccount的任何代码------只要他自己相关的接口是稳定的!

​ 我们甚至还可以滚动回来!比如说我们小小的翻新一下Command(假设我们真的笃定Command是要支持撤回的)

cpp 复制代码
struct Command { 
	virtual void call() = 0; 
	virtual void undo() = 0; 
};

​ 现在我们自然可以根据我们实现的逻辑的撤回操作依次的完成Command的接口,最后我们就会组合成一个非常具备代码整洁的命令链条------还是支持撤回操作的那种!

组合我们的命令:结合组合模式+命令模式

​ 一堆Command的有机组合是不是也是一个Command,或者说Command的正交组合显然还是一个Command。那么,我们就有理由编写出一个更好的组合Command

cpp 复制代码
struct ComposedBankCommand : Command
{
    // register commands
    void call() override {
      	for(auto& cmd : composers)  cmd->call();
    };
    
    void undo() override {
        for(auto& cmd : composers)  cmd->call();
    }
    
private:
    vector<Command*>	composers;
};

​ 但好像不对?如果我们中间的一个command失败了,其他的干脆就不应该调用------当然对于并行式的Command蔟完全没问题。这个是逻辑设计的差异。解决这个的办法也很好说------Command内部维护一个是否成功的操作就好了嘛!

总结:

解决什么问题

命令模式要解决的核心问题是把"动作"从调用者中剥离出来,使动作成为可传递、可存储、可组合、可撤销的对象。当程序中出现需要延迟执行、排队执行、撤销/重做、日志回放、网络传输或组合多个操作等需求时,直接在调用处硬编码调用逻辑会导致耦合、难以扩展与难以控制。命令模式把对某个接口/对象的"操作"封装成单独的对象(Command),从而把请求者与执行者解耦,同时把操作本身作为一等公民来处理(队列、日志、回滚、组合......都可以做)。

如何解决

命令模式通过定义一个统一的命令接口(比如 call() / execute(),必要时还加上 undo() / redo())来表示"要做的事"。每个具体命令包含执行该操作所需的接收者引用和参数。调用方只负责生成或提交命令对象,不直接操作接收者;命令可以被放入队列、写入日志、传到远端、组合成宏命令或在稍后执行。为支持撤销/补偿,命令可以维护执行前的状态(或借助 Memento),或者提供一个 undo() 方法做反向操作。为了支持异步、可靠性或回放,还可以给命令增加序列化/日志化能力。


各个变种的优劣对比
变种 描述 优点 缺点 典型适用场景
基本命令(Basic Command) 最简形式:命令对象封装接收者和参数,实现 execute() 实现简单,耦合低,便于扩展与测试。 只适合同步、一次性调用,缺少撤销/持久化支持。 简单的解耦、延迟执行需求。
可撤销命令(Undoable Command) 命令实现 execute()undo() 或保存 Memento 做回滚。 支持撤销/重做,用户体验好(编辑器、事务界面)。 需要额外保存状态或实现反向操作,设计与边界条件复杂。 文本编辑、图形编辑、事务局部回退。
组合命令 / 宏命令(Composite / Macro) 把多个命令组合成一个命令;支持原子或顺序执行。 易于复用、批量操作、一次执行多个子操作。 失败处理复杂(部分成功如何回滚);错误传播要设计清楚。 批处理、事务化工作流、复合操作。
相关推荐
郝学胜_神的一滴19 小时前
CMake 034:生成器表达式:解耦构建时序、精简分支逻辑的终极利器
c++·cmake
咖啡八杯1 天前
GoF设计模式——中介者模式
java·后端·spring·设计模式
见过夏天1 天前
C++ 基础入门完全指南
c++
胡萝卜术2 天前
从“分数打架”到“排名投票”:为什么你的ChatBI必须用RRF?
算法·设计模式·面试
亦暖筑序3 天前
Java 8老系统Browser Agent实战:三层拦截把AI操作后台变成可审计流程
java·后端·设计模式
用户805533698033 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
BadBadBad__AK3 天前
线段树维护区间 k 次方和
c++·数学·算法·stl
卷无止境4 天前
Eigen 库如何借助 OpenMP 加速计算
c++·后端
卷无止境4 天前
OpenMPI、MPICH 与 OpenMP:关系、核心概念与架构全解
c++·后端
郝学胜_神的一滴5 天前
CMake 30:循环语法全解|foreach_while双循环精讲、迭代技巧与实战避坑指南
c++·cmake