C++职责链模式设计理念插图

C++职责链模式设计理念:让请求在对象链上优雅旅行

大家好,今天我想和大家深入聊聊C++中的职责链模式(Chain of Responsibility)。这个模式在我处理那些“不知道找谁处理,或者需要多个对象依次尝试”的业务场景时,简直是个神器。它就像公司里的审批流程,一个请求(比如报销单)从基层员工开始,如果无权处理,就传递给经理,经理不行再给总监,直到有人能处理为止。这种解耦发送者和接收者的思想,让代码的灵活性大大提升。

一、为什么我们需要职责链模式?

记得之前我维护过一个旧系统的消息处理模块。最初的代码是一大坨复杂的if-else if或者switch-case语句,用来根据消息类型决定由哪个处理器处理。每当要新增一种消息类型,我就得战战兢兢地去修改那个庞大的中心判断函数,生怕影响了其他逻辑。这种代码耦合度高,难以维护和扩展。

职责链模式正是为了解决这个问题而生。它的核心设计理念是:将多个处理对象(处理器)连成一条链,请求沿着这条链传递,直到有一个对象处理它为止。 发送者无需关心最终由谁处理,从而实现了请求发送者和接收者的解耦。每个处理器只关注自己是否能够处理,不能处理则传递给下一个,这非常符合“单一职责”原则。

二、职责链模式的结构与角色

在C++中实现职责链,通常涉及以下几个关键角色:

  1. Handler(抽象处理者):定义处理请求的接口,并持有下一个处理者的引用(后继者)。这是整个模式的骨架。
  2. ConcreteHandler(具体处理者):实现抽象处理者的接口,判断自己是否能处理请求。能则处理,不能则转发给后继者。
  3. Client(客户端):创建职责链,并向链的头部提交请求。

下面,我们通过一个最经典的例子——费用报销审批流程,来把理论落地。假设报销金额小于1000元由经理批,小于5000元由总监批,5000元以上需要CEO审批。

三、实战:构建一个报销审批链

首先,我们定义抽象处理者类 Approver。这里我选择在基类中实现设置后继者的逻辑,这是纯虚函数,具体处理者必须实现如何处理请求。

#include 
#include 
#include 

// 抽象处理者:审批人
class Approver {
public:
    virtual ~Approver() = default;
    
    // 设置后继者
    void setSuccessor(std::shared_ptr successor) {
        successor_ = successor;
    }
    
    // 处理请求的纯虚函数
    virtual void processRequest(int amount) = 0;

protected:
    std::shared_ptr successor_; // 指向下一个处理者的指针
};

接下来,我们实现具体的处理者:经理(Manager)、总监(Director)和CEO。这里有个小技巧:我通常会在具体处理者的processRequest函数中,将无法处理的请求明确地传递给后继者,这样逻辑更清晰。

// 具体处理者:经理
class Manager : public Approver {
public:
    void processRequest(int amount) override {
        if (amount <= 1000) {
            std::cout << "经理批准了 " << amount << " 元的报销单。" << std::endl;
        } else if (successor_ != nullptr) {
            std::cout << "经理无权限,转交给上级。" <processRequest(amount); // 传递给后继者
        } else {
            std::cout << "金额 " << amount << " 元过高,无人能处理。" << std::endl;
        }
    }
};

// 具体处理者:总监
class Director : public Approver {
public:
    void processRequest(int amount) override {
        if (amount <= 5000) {
            std::cout << "总监批准了 " << amount << " 元的报销单。" << std::endl;
        } else if (successor_ != nullptr) {
            std::cout << "总监无权限,转交给上级。" <processRequest(amount);
        } else {
            std::cout << "金额 " << amount << " 元过高,无人能处理。" << std::endl;
        }
    }
};

// 具体处理者:CEO
class CEO : public Approver {
public:
    void processRequest(int amount) override {
        if (amount <= 10000) {
            std::cout << "CEO批准了 " << amount << " 元的报销单。" << std::endl;
        } else {
            std::cout << "金额 " << amount << " 元过高,即使CEO也无法批准。" << std::endl;
        }
        // CEO是链的末端,没有后继者,或者也可以选择不处理。
    }
};

现在,让我们在客户端(main函数)中组装这条链并测试。这里我使用std::shared_ptr来管理对象生命周期,避免内存泄漏的坑。

int main() {
    // 1. 创建具体的处理者
    auto manager = std::make_shared();
    auto director = std::make_shared();
    auto ceo = std::make_shared();
    
    // 2. 构建职责链:经理 -> 总监 -> CEO
    manager->setSuccessor(director);
    director->setSuccessor(ceo);
    // CEO是链尾,可以不设置后继者
    
    std::cout << "=== 报销审批流程测试 ===" <processRequest(800);   // 经理处理
    std::cout << "--------" <processRequest(3500);  // 经理转总监处理
    std::cout << "--------" <processRequest(8500);  // 经理转总监转CEO处理
    std::cout << "--------" <processRequest(12000); // 传递到CEO也无法处理
    
    return 0;
}

运行这个程序,你会看到请求如何根据金额大小,在“经理-总监-CEO”这条链上流动,每个处理器各司其职。这种设计的优雅之处在于,如果未来要增加一个“副总裁”的审批环节,我只需要新建一个VicePresident类,并在客户端调整链的组装顺序即可,完全不需要修改已有的处理器类代码。

四、深入思考与踩坑提示

通过上面的例子,相信你已经掌握了基础。但在实际项目中,还有一些细节需要注意:

1. 链的终止条件: 必须确保链的末尾有合理的处理(或终止)逻辑,否则请求可能“石沉大海”,导致程序行为异常。就像上面的CEO类,它明确处理了它能处理的,并拒绝了过高的金额。

2. 性能考量: 如果链非常长,且大部分请求都需要传递到链尾才能处理,那么性能可能会成为问题。在设计时,可以考虑将最可能被处理的处理器放在链的前面。

3. 请求可能未被处理: 这是职责链模式的固有特点。客户端需要意识到,提交的请求有可能没有任何处理器接手。根据业务需求,你可能需要在链的末端添加一个“默认处理器”或“异常抛出器”。

4. 动态修改链: 职责链可以在运行时动态改变,这既是优点也是复杂度来源。确保在修改链结构(比如增加、删除或重排处理器)时,处理好对象间的引用关系,避免出现循环引用或空指针访问。

五、总结:何时该用职责链模式?

回顾我的使用经验,职责链模式特别适用于以下场景:

  • 多级处理或审批流程(如我们刚实现的例子)。
  • 事件冒泡机制(如GUI中的事件处理)。
  • 中间件管道(如Web服务器中的请求过滤器链,每个过滤器检查请求并决定是否传递给下一个)。
  • 当你想避免在发送者和多个接收者之间建立硬编码的、复杂的关联时。

它的精髓在于通过“链”这个结构,将处理逻辑分散到多个独立的类中,降低了耦合度,增强了系统的可扩展性和可维护性。下次当你面对一堆复杂的条件分支,并且感觉它们各自代表独立的业务逻辑时,不妨考虑一下职责链模式,它很可能就是让代码变得更优雅的那把钥匙。

希望这篇结合实战和踩坑经验的分享能帮到你。在C++的世界里,理解设计模式的思想远比死记硬背类图重要。多思考,多实践,你一定能用得更好。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。