精读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) 把多个命令组合成一个命令;支持原子或顺序执行。 易于复用、批量操作、一次执行多个子操作。 失败处理复杂(部分成功如何回滚);错误传播要设计清楚。 批处理、事务化工作流、复合操作。
相关推荐
AA陈超2 小时前
虚幻引擎UE5专用服务器游戏开发-32 使用Gameplay Tags阻止连招触发
c++·游戏·ue5·游戏引擎·虚幻
金色熊族3 小时前
ubuntu20.04编译qt源码5.15.3
linux·c++·qt
智能化咨询3 小时前
【C++】异常介绍:高级应用与性能优化
c++
丶Darling.3 小时前
26考研 | 王道 | 计算机组成原理 | 二、数据的表示和运算
笔记·学习·计算机组成原理
丰锋ff3 小时前
2009 年真题配套词汇单词笔记(考研真相)
笔记·学习·考研
呆瑜nuage4 小时前
c++之AVL树
c++
星空寻流年4 小时前
设计模式第六章(观察者模式)
网络·观察者模式·设计模式
J.Kuchiki5 小时前
【PostgreSQL内核学习:哈希聚合(HashAgg)执行流程与函数调用关系分析】
学习·postgresql
磨十三5 小时前
C++ 类型转换全面解析:从 C 风格到 C++ 风格
java·c语言·c++