设计模式之责任链模式(二): 实现方式

C++设计模式专栏:http://t.csdnimg.cn/8Ulj3
相关文章系列

设计模式之责任链模式(一)-CSDN博客

目录

1.引言

2.实现方式1

3.实现方式2

4.总结


1.引言

责任链设计模式(Chain of Responsibiliy DesignPattern)简称职责链模式。在GOF的《设计模式:可复用面向对象软件的基础》中,它是这样定义的:将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求;将这些接收对象串成一条链,并沿者这条链传递这个请求,直到链上的某个接收对象能够处理它为止(Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it )。

在职责链模式中,多个处理器(也就是定义中所说的"接收对象")依次处理同一个请求。一个请求首先经过A处理器处理,然后,这个请求被传递给B处理器,B处理器处理完后再将其传递给C处理器,以此类推,形成一个链条。因为链条上的每个处理器各自承担各自职责,所以称为职责链模式。

职责链模式有多种实现方式,这里介绍两种常用的。

2.实现方式1

实现方式的代码如下所示。其中,Handler类是所有处理器类的抽象父类,handle()是抽象方法。每个具体的处理器类(HandlerA、HandlerB)的handle()函数的代码结构类似,如果某个处理器能够处理该请求,就不继续往下传递;如果它不能处理,则交由后面的处理器处理(也就是调用successor.handle())。HandlerChain类表示处理器链,从数据结构的角度来看,它就是一个记录了链头、链尾的链表。其中,记录链尾是为了方便添加处理器。

cpp 复制代码
#pragma once
#include <memory>
#include <vector>

class IHandler
{
protected:
	IHandler* m_successor;
public:
	void setSuccessor(IHandler* successor) {
		m_successor = successor;
	}
	virtual void handle() = 0;
};

class HandlerA : public IHandler
{
public:
	//...
	//@override
	void handle() override {
		bool bHandle = false;
		//...
		if (!bHandle && m_successor) {
			m_successor->handle();
		}
		//...
	}
};

class HandlerB : public IHandler
{
public:
	//...
	//@override
	void handle() override {
		bool bHandle = false;
		//...
		if (!bHandle && m_successor) {
			m_successor->handle();
		}
		//...
	}
};

class HandlerChain {
private:
	IHandler* head = nullptr;
	IHandler* tail = nullptr;
public:
	void addHandler(IHandler* handler) {
		handler->setSuccessor(nullptr);
		if (head == nullptr) {
			head = handler;
			tail = handler;
			return;
		}
		tail->setSuccessor(handler);
		tail = handler;
	}
	void handle() {
		if (head != nullptr) {
			head->handle();
		}
	}
};

//使用举例
int main() {
	std::unique_ptr<IHandler> pHandleA(new HandlerA());
	std::unique_ptr<IHandler> pHandleB(new HandlerB());
	HandlerChain chain;

	chain.addHandler(pHandleA.get());
	chain.addHandler(pHandleB.get());

	chain.handle();

    return 1;
}

实际上,上面的代码实现不够优雅,因为处理器类的handle()函数不仅包含自己的业务逻辑。还包含对下一个处理器的调用(对应代码中的successor.handle())。如果一个不熟悉这种代码结构的程序员想要在其中添加新的处理器类,那么很有可能忘记在handle()函数中调用successor.handle(),这就会导致代码出现bug。

设计模式之模板方法模式-CSDN博客

针对这个问题,我们对代码进行重构,利用模版方法模式,将调用successor.handle()的逻辑从处理器中剥离出来,放到抽象父类中。这样,处理器类只需要实现自己的业务逻辑。重构之后的代码如下所示:

cpp 复制代码
class IHandler
{
protected:
	IHandler* m_successor;
public:
	void setSuccessor(IHandler* successor) {
		m_successor = successor;
	}
	void handle() {
		bool bHandled = doHandle();
		if (!bHandled && m_successor) {
			m_successor->handle();
		}
	}
protected:
	virtual bool doHandle() = 0;
};

class HandlerA : public IHandler
{
protected:
	//...
	//@override
	bool doHandle() override {
		bool bHandle = false;
		//...
		return bHandle;
	}
};

class HandlerB : public IHandler
{
protected:
	//...
	//@override
	bool doHandle() override {
		bool bHandle = false;
		//...
		return bHandle;
	}
};

class HandlerChain {
private:
	IHandler* head = nullptr;
	IHandler* tail = nullptr;
public:
	void addHandler(IHandler* handler) {
		handler->setSuccessor(nullptr);
		if (head == nullptr) {
			head = handler;
			tail = handler;
			return;
		}
		tail->setSuccessor(handler);
		tail = handler;
	}
	void handle() {
		if (head != nullptr) {
			head->handle();
		}
	}
};

int  main() {
	std::unique_ptr<IHandler> pHandleA(new HandlerA());
	std::unique_ptr<IHandler> pHandleB(new HandlerB());
	HandlerChain chain;

	chain.addHandler(pHandleA.get());
	chain.addHandler(pHandleB.get());

	chain.handle();

    return 1;
}

3.实现方式2

实现代码如下所示,这种实现方式更加简单,其中HandlerChain 类用数组而非链表来保存所有处理器类,并且在HandlerChain类的handle()函数中,依次调用每个处理器类的 handle()函数。

cpp 复制代码
class IHandler
{
public:
	virtual bool handle() = 0;
};

class HandlerA : public IHandler
{
public:
	//...
	//@override
	bool handle() override {
		bool bHandle = false;
		//...
		return bHandle;
	}
};

class HandlerB : public IHandler
{
public:
	//...
	//@override
	bool handle() override {
		bool bHandle = false;
		//...
		return bHandle;
	}
};

class HandlerChain {
private:
	std::vector<IHandler*> m_vecHandler;
public:
	void addHandler(IHandler* handler) {
		m_vecHandler.push_back(handler);
	}
	void handle() {
		for (auto& it : m_vecHandler) {
			if (it->handle()) {
				break;
			}
		}
	}
};

int main() {
	std::unique_ptr<IHandler> pHandleA(new HandlerA());
	std::unique_ptr<IHandler> pHandleB(new HandlerB());
	HandlerChain chain;

	chain.addHandler(pHandleA.get());
	chain.addHandler(pHandleB.get());

	chain.handle();

    return 1;
}

在GoF合著的《设计模式:可复用面向对象软件的基础》给出的职责链模式的定义中。如果处理器链上的某个处理器能够处理这个请求,就不会继续往下传递请求。实际上,职责链模式还有一种变体,那就是请求会被所有处理器都处理一遍,不存在中途终止的情况。这种变体也有两种实现方式: 用链表存储处理器类和用数组存储处理器类,与上面两种实现方式类似稍加修改即可。这里只给出用链表存储处理器类的实现方式,代码如下所示。对于用数组存储处理器类的实现方式,读者可对照上面的实现自行修改。

cpp 复制代码
class IHandler
{
protected:
	IHandler* m_successor;
public:
	void setSuccessor(IHandler* successor) {
		m_successor = successor;
	}
	void handle() {
		doHandle();
		if (m_successor) {
			m_successor->handle();
		}
	}
protected:
	virtual void doHandle() = 0;
};

class HandlerA : public IHandler
{
protected:
	//...
	//...
	//@override
	void doHandle() override {
		//...
	}
};

class HandlerB : public IHandler
{
protected:
	//...
	//@override
	void doHandle() override {
		//...
	}
};

class HandlerChain {
private:
	IHandler* head = nullptr;
	IHandler* tail = nullptr;
public:
	void addHandler(IHandler* handler) {
		handler->setSuccessor(nullptr);
		if (head == nullptr) {
			head = handler;
			tail = handler;
			return;
		}
		tail->setSuccessor(handler);
		tail = handler;
	}
	void handle() {
		if (head != nullptr) {
			head->handle();
		}
	}
};

int main() {
	std::unique_ptr<IHandler> pHandleA(new HandlerA());
	std::unique_ptr<IHandler> pHandleB(new HandlerB());
	HandlerChain chain;

	chain.addHandler(pHandleA.get());
	chain.addHandler(pHandleB.get());

	chain.handle();

    return 1;
}

4.总结

尽管我们给出了典型的职责链模式的代码实现,但在实际的开发中,我们还是要具体问题具体对待,因为职责链模式的代码实现会根据需求的不同而有所变化。实际上,这一点对于有设计模式都适用。

相关推荐
我变成萤火虫1 小时前
河南萌新联赛2026第(四)场:南阳理工学院
数据结构·c++·算法·贪心算法·stl·动态规划
To_OC3 小时前
从一行入口代码啃透 NestJS:工厂模式、装饰器和依赖注入是怎么串起来的
后端·设计模式·nestjs
余额瞒着我当琳5 小时前
C++--深拷贝三件套 + swap + 写时拷贝 + vector 扩容 + reserve
java·开发语言·c++
东方小月5 小时前
从零开发一个 Coding Agent(十二):实现版本化 JSONL 与真实 CLI 入口
前端·设计模式·前端框架
ShineWinsu6 小时前
对于C++:布隆过滤器(bloomfilter)的详细解析
c++·面试·位图·位运算·布隆过滤器·海量数据·比特位
程序员贺加贝7 小时前
轻制造SaaS的生产闭环建模-BOM工单领料报工质检与入库
java·设计模式·架构
嘟嘟07179 小时前
SDD 规范驱动开发:从 vibe coding 崩盘到"两次创造"
设计模式·代码规范·vibecoding
牛油果子哥q10 小时前
C++内存模型与深浅拷贝万字详解:栈堆静态内存布局、深浅拷贝底层差异、内存泄漏根治、拷贝崩溃踩坑、手写深拷贝实战
java·开发语言·c++
大鹏的NLP博客10 小时前
通用 Linux 嵌入式板端 C/C++ ABI 与 Glibc 依赖治理规范
linux·c++·交叉编译
ysa05103011 小时前
c++常用自带函数用法与注意
c++·笔记·算法