1 责任链模式的概念

责任链模式(Chain of Responsibility Pattern)是一种行为设计模式,它允许对象以链式的方式组织起来,以便对请求进行处理。这种模式为多个对象处理同一请求提供了一个灵活的机制,而无需在发送者和多个请求处理者之间显式地指定耦合关系。

在责任链模式中,每个处理者对象都包含对下一个处理者的引用,从而形成了一个处理者链。当一个请求到达某个处理者时,该处理者会先判断自己是否能处理该请求。如果能处理,则直接处理;如果不能处理,则将该请求传递给链中的下一个处理者。这样,请求会沿着链一直传递,直到被某个处理者处理为止。

在 C++ 中,责任链模式通常包含以下几个关键部分:

(1)抽象处理者(Handler):

  • 定义了一个处理请求的接口,通常包含一个方法用于处理请求,并持有对下一个处理者的引用。
  • 抽象处理者可以是一个基类或接口,具体处理者将继承或实现这个基类或接口。

(2)具体处理者(Concrete Handler):

  • 继承或实现抽象处理者,并实现了处理请求的具体逻辑。
  • 具体处理者可以包含对下一个处理者的引用,以便在无法处理请求时将其传递给下一个处理者。
  • 具体处理者也可以包含一个条件判断,以确定是否应该处理请求或将其传递给下一个处理者。

(3)客户端(Client):

  • 创建处理者链,并将请求发送给链中的第一个处理者。
  • 客户端不需要知道链中具体的处理者数量或类型,只需将请求发送给链的起点即可。

2 责任链模式的实现步骤

在 C++ 实现责任链模式的实现步骤如下:

(1)定义抽象处理者接口:

  • 创建一个抽象类(或接口),定义处理请求的方法,通常命名为 HandleRequest 或类似名称。
  • 在这个抽象类中,包含一个指向下一个处理者的指针或引用,用于构建处理者链。

(2)实现具体处理者类:

  • 创建继承自抽象处理者接口的具体处理者类。
  • 在具体处理者类中,实现 HandleRequest 方法,根据业务逻辑判断是否处理请求。
  • 如果当前处理者能够处理请求,则处理并结束流程;如果不能处理,则调用下一个处理者的 HandleRequest 方法。
  • 在每个具体处理者类中,包含对下一个处理者的引用,并在构造函数或设置方法中设置它。

(3)构建处理者链:

  • 在客户端代码中,创建具体处理者对象,并按照需要的顺序将它们连接起来。
  • 每个处理者都需要知道它之后的下一个处理者,可以通过设置方法(如 SetNext)来实现。

(4)发送请求:

  • 在客户端中,通过调用链中第一个处理者的 HandleRequest 方法来发送请求。
  • 请求将沿着处理者链传递,直到某个处理者处理了请求,或者请求到达了链的末尾而未被处理。

(5)处理异常情况:

  • 在实现责任链模式时,需要考虑异常情况的处理。
  • 如果请求在链中未被任何处理者处理,可以设计兜底逻辑,例如一个默认的处理者或者返回错误信息。

如下为样例代码:

#include <iostream>  
#include <memory>  

// 抽象处理者  
class Handler {
public:
	virtual ~Handler() {}
	virtual void HandleRequest(int request) = 0;
	virtual void SetNext(std::unique_ptr<Handler> next) = 0;

protected:
	std::unique_ptr<Handler> next;
};

// 具体处理者A  
class ConcreteHandlerA : public Handler {
public:
	void SetNext(std::unique_ptr<Handler> next) override {
		this->next = std::move(next);
	}

	void HandleRequest(int request) override {
		if (request >= 0 && request < 10) {
			std::cout << "ConcreteHandlerA handled request " << request << std::endl;
		}
		else if (next) {
			next->HandleRequest(request);
		}
		else {
			std::cout << "No handler could process request " << request << std::endl;
		}
	}
};

// 具体处理者B  
class ConcreteHandlerB : public Handler {
public:
	void SetNext(std::unique_ptr<Handler> next) override {
		this->next = std::move(next);
	}

	void HandleRequest(int request) override {
		if (request >= 10 && request < 20) {
			std::cout << "ConcreteHandlerB handled request " << request << std::endl;
		}
		else if (next) {
			next->HandleRequest(request);
		}
		else {
			std::cout << "No handler could process request " << request << std::endl;
		}
	}
};

// 客户端代码  
int main() 
{
	// 创建处理者对象并使用智能指针管理  
	std::unique_ptr<Handler> handlerA = std::make_unique<ConcreteHandlerA>();
	std::unique_ptr<Handler> handlerB = std::make_unique<ConcreteHandlerB>();

	// 构建处理者链  
	handlerA->SetNext(std::move(handlerB));

	// 发送请求给链的第一个处理者  
	handlerA->HandleRequest(5);  // 由ConcreteHandlerA处理  
	handlerA->HandleRequest(15); // 由ConcreteHandlerB处理  
	handlerA->HandleRequest(25); // 无人处理,输出错误信息  

	return 0;
}

上面代码的输出为:

ConcreteHandlerA handled request 5
ConcreteHandlerB handled request 15
No handler could process request 25

这个样例使用了 std::unique_ptr 来管理处理者对象的生命周期,确保在链中传递时不会发生内存泄漏。每个具体处理者(ConcreteHandlerA 和 ConcreteHandlerB)都实现了 HandleRequest 方法来处理请求,并通过调用 SetNext 方法来设置下一个处理者。如果当前处理者不能处理请求,它将请求传递给链中的下一个处理者。如果在链的末尾仍然没有处理者能够处理请求,则输出一个错误信息。

注意:在 main 函数中,使用 std::make_unique 来创建具体处理者的实例,并使用 std::move 来将智能指针的所有权从一个对象传递到另一个对象,从而构建处理者链。这样做可以确保在链中正确地管理对象的生命周期,并避免不必要的拷贝操作。

3 责任链模式的应用场景

C++责任链模式的应用场景主要集中在需要多个对象处理同一请求,且具体处理对象在运行时动态决定的场合。以下是一些具体的应用场景:

(1)业务流程处理: 如采购审批流程、请假流程等,这些流程可能涉及多个部门和人员,每个部门和人员都有自己的处理逻辑和权限。使用责任链模式可以方便地实现这些流程的自动化处理。

(2)多个对象处理同一请求: 当系统中有多个对象都可以处理某一类请求,但具体由哪个对象处理需要在运行时决定时,可以使用责任链模式。例如,在一个复杂的业务逻辑中,可能有多个处理者需要根据不同的条件来处理同一请求。

(3)请求处理者不明确: 在不明确指定接收者的情况下,可以向多个对象中的一个提交请求。责任链模式允许请求在多个处理者之间传递,直到有处理者能够处理该请求为止。这种模式在处理异常、事件或消息传递时特别有用。

(4)动态指定处理者: 需要动态地指定一组对象来处理请求时,也可以使用责任链模式。例如,在某些情况下,处理者的数量或顺序可能会根据系统的运行状态或配置而发生变化。

3.1 责任链模式应用于业务流程处理

下面是一个 C++ 中使用智能指针和责任链模式应用于业务流程处理的示例。在这个例子中,假设有一个请假审批流程,它包含多个审批环节,如部门主管审批、人事审批等。每个审批环节都是一个处理者,它们按照顺序组成责任链。

首先,定义抽象处理者接口和具体处理者类:

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

// 抽象处理者  
class LeaveApprovalHandler {
public:
	virtual ~LeaveApprovalHandler() {}
	virtual void HandleRequest(const std::string& employee, int days) = 0;
	virtual void SetNext(std::unique_ptr<LeaveApprovalHandler> next) = 0;

protected:
	std::unique_ptr<LeaveApprovalHandler> next;
};

// 具体处理者:部门主管审批  
class DepartmentHeadHandler : public LeaveApprovalHandler {
public:
	void SetNext(std::unique_ptr<LeaveApprovalHandler> next) override {
		this->next = std::move(next);
	}

	void HandleRequest(const std::string& employee, int days) override {
		if (days <= 3) {
			std::cout << "Department Head approved leave for " << employee << " for " << days << " days." << std::endl;
		}
		else {
			if (next) {
				next->HandleRequest(employee, days);
			}
			else {
				std::cout << "No further approval handler available for " << employee << " for " << days << " days." << std::endl;
			}
		}
	}
};

// 具体处理者:人事审批  
class HRHandler : public LeaveApprovalHandler {
public:
	void SetNext(std::unique_ptr<LeaveApprovalHandler> next) override {
		this->next = std::move(next);
	}

	void HandleRequest(const std::string& employee, int days) override {
		if (days > 3 && days <= 7) {
			std::cout << "HR approved leave for " << employee << " for " << days << " days." << std::endl;
		}
		else if (next) {
			next->HandleRequest(employee, days);
		}
		else {
			std::cout << "No further approval handler available for " << employee << " for " << days << " days." << std::endl;
		}
	}
};

// 最终处理者:总经理审批  
class GeneralManagerHandler : public LeaveApprovalHandler {
public:
	void HandleRequest(const std::string& employee, int days) override {
		if (days > 7) {
			std::cout << "General Manager approved leave for " << employee << " for " << days << " days." << std::endl;
		}
		else {
			std::cout << "Leave request for " << employee << " for " << days << " days was not approved by General Manager." << std::endl;
		}
	}

	void SetNext(std::unique_ptr<LeaveApprovalHandler> /*next*/) override {
		// 最终处理者后面没有其他处理者  
	}
};

接下来,在客户端代码中构建责任链,并发送请求:

int main() 
{
	// 创建具体处理者对象并使用智能指针管理  
	std::unique_ptr<LeaveApprovalHandler> departmentHead = std::make_unique<DepartmentHeadHandler>();
	std::unique_ptr<LeaveApprovalHandler> hr = std::make_unique<HRHandler>();
	std::unique_ptr<LeaveApprovalHandler> generalManager = std::make_unique<GeneralManagerHandler>();

	// 构建处理者链  
	hr->SetNext(std::move(generalManager));
	departmentHead->SetNext(std::move(hr));

	// 发送请假请求给链的第一个处理者  
	departmentHead->HandleRequest("Alice", 2);     // 部门主管审批  
	departmentHead->HandleRequest("Bob", 5);       // 人事审批  
	departmentHead->HandleRequest("Charlie", 10);  // 总经理审批  

	return 0;
}

上面代码的输出为:

Department Head approved leave for Alice for 2 days.
HR approved leave for Bob for 5 days.
General Manager approved leave for Charlie for 10 days.

在这个示例中,LeaveApprovalHandler 是抽象处理者接口,DepartmentHeadHandler、HRHandler 和 GeneralManagerHandler 是具体处理者类,它们分别代表了不同层级的审批者。每个处理者都实现了 HandleRequest 方法来处理请求,并通过 SetNext 方法将请求传递给链中的下一个处理者。

运行这个程序,可以看到根据员工请假的天数,不同的处理者会处理或传递请求。如果请求到达链的末尾仍未被处理,则输出相应的消息。

3.2 责任链模式应用于多个对象处理同一请求

在C++中,责任链模式用于多个对象处理同一请求的场景时,通常每个对象都表示链中的一个节点,负责处理请求或将其传递给链中的下一个节点。下面是一个构建和处理责任链的示例:

首先,定义处理请求的抽象接口和具体处理者类:

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

// 抽象请求处理者  
class Handler {
public:
	virtual ~Handler() {}

	// 处理请求  
	virtual void HandleRequest(int type, const std::string& request) = 0;

	// 设置下一个处理者  
	virtual void SetNext(std::unique_ptr<Handler> next) = 0;

protected:
	std::unique_ptr<Handler> next;
};

// 具体的请求处理者A  
class HandlerA : public Handler {
public:
	void HandleRequest(int type, const std::string& request) override {
		if (0 == type) {
			std::cout << "HandlerA processing request: " << request << std::endl;
		}
		else {
			if (next) {
				next->HandleRequest(type,request);
			}
			else {
				std::cout << "Request not processed: " << request << std::endl;
			}
		}
	}

	void SetNext(std::unique_ptr<Handler> next) override {
		this->next = std::move(next);
	}
};

// 具体的请求处理者B  
class HandlerB : public Handler {
public:
	void HandleRequest(int type, const std::string& request) override {
		if (1 == type) {
			std::cout << "HandlerB processing request: " << request << std::endl;
		}
		else {
			if (next) {
				next->HandleRequest(type, request);
			}
			else {
				std::cout << "Request not processed: " << request << std::endl;
			}
		}
	}

	void SetNext(std::unique_ptr<Handler> next) override {
		this->next = std::move(next);
	}
};

// 终端处理者(没有下一个处理者)  
class HandlerTerminal : public Handler {
public:
	void HandleRequest(int type, const std::string& request) override {
		std::cout << "HandlerTerminal processing request: " << request << std::endl;
	}

	void SetNext(std::unique_ptr<Handler> /*next*/) override {
		// 终端处理者不需要设置下一个处理者  
	}
};

然后,在客户端代码中,可以创建处理者对象并使用智能指针来构建责任链:

int main() 
{
	// 创建处理者对象  
	std::unique_ptr<Handler> handlerA = std::make_unique<HandlerA>();
	std::unique_ptr<Handler> handlerB = std::make_unique<HandlerB>();
	std::unique_ptr<Handler> handlerTerminal = std::make_unique<HandlerTerminal>();

	// 构建责任链  
	handlerB->SetNext(std::move(handlerTerminal));
	handlerA->SetNext(std::move(handlerB));

	// 发送请求  
	handlerA->HandleRequest(0, "Request 1");
	handlerA->HandleRequest(1, "Request 2");
	handlerA->HandleRequest(2, "Request 3");

	return 0;
}

上面代码的输出为:

HandlerA processing request: Request 1
HandlerB processing request: Request 2
HandlerTerminal processing request: Request 3

在这个例子中,Handler 是一个抽象接口,HandlerA 和 HandlerB 是实现了这个接口的具体类,它们根据某些条件来决定是否处理请求,或者将请求传递给链中的下一个处理者。HandlerTerminal 是一个终端处理者,它没有下一个处理者,并且总是处理传递给它的请求。

每个处理者内部都持有一个指向下一个处理者的 std::unique_ptr 智能指针。当处理者决定不处理请求时,它会调用 next->HandleRequest(request) 来将请求传递给链中的下一个处理者。如果当前处理者是链中的最后一个(即 next 为空),则输出一条消息表示请求未被处理。

4 责任链模式的优点与缺点

C++ 责任链模式的优点主要包括:

(1)耦合度降低: 通过将请求的处理分散到多个处理者对象中,责任链模式降低了客户端与具体处理者之间的耦合度。客户端只需要将请求发送给链中的第一个处理者,而不需要知道链的具体结构或每个处理者的具体实现。

(2)扩展性好: 如果需要添加新的处理逻辑,只需要创建新的处理者类并将其添加到链中即可,无需修改已有的代码。这使得系统的扩展变得非常容易和灵活。

(3)动态性增强: 处理者对象之间的顺序可以在运行时动态调整,根据业务逻辑的需要,可以方便地改变请求的处理流程。

(4)支持多重处理: 一个请求可以依次被多个处理者处理,每个处理者都可以对请求进行部分处理或添加额外的信息,然后将请求传递给下一个处理者。

然而,C++ 责任链模式也存在一些缺点:

(1)性能开销: 由于请求需要在链中依次传递,如果链很长或者处理者的处理逻辑复杂,可能会导致性能下降。特别是在处理大量请求时,这种开销可能会变得显著。

(2)调试困难: 由于请求在链中传递,当出现问题时,定位问题可能会比较困难。需要逐个检查每个处理者以找出问题所在。

(3)链的配置和管理: 构建和维护责任链可能需要额外的代码和逻辑来管理处理者之间的依赖关系和顺序。这可能会增加代码的复杂性和维护成本。

(4)可能导致请求被忽略: 如果没有正确处理链的终止条件或没有设置终端处理者,请求可能会在链中丢失或被忽略,导致业务逻辑不完整或错误。

03-16 15:29