我用过的设计模式(4)-- 责任链模式

2021-09-18 10:13:48 浏览数 (1)

文章目录

    • 前言
    • 责任链模式
      • 示例代码
    • 第一个epoll模块
    • 使用责任链模式优化过的epoll模块

前言

刚接触责任链的时候,我不是很喜欢这个模式,因为我不知道它能拿来干什么用啊。

直到后来写那个FTP项目的时候,我用责任链 调停者优化了我的epoll模块之后,我爱死这个模式了!!!


责任链模式

什么是责任链模式呢?我们来看个小故事:

最近给团队里的程序员们分了个等级,模仿着阿里的那套模式,将我们团队里人分为P6/P7/P8/P9/P10。

来活儿的时候呢,就这样分配,根据难度初步估计,分配给对应的等级的程序员去做,难度等级7级就给P7,8级就给P8。

但是这样会有个什么问题呢?其实也没啥问题,就是这好像不需要用类,直接在场景main里面放一堆的if来判断就好了。

那如果分配到当前等级的人他不收呢?那就得移交到下一个等级去,这要是用ifelse来判断,可想而知代码将会有多么的拥挤。

而且将本不属于场景类的任务强加到场景类之中,似乎也不合情理。

所以,我们采用这样一个方法:创建一个责任类,将各级程序员按等级排序,当有任务来临时,依次向后,如果能接那就接了,轮到最后还没人接,那就退了,默认没人接。

这,就是责任链模式。

这个图是很简单的嘞:

乍一看,平平无奇,甚至还会感觉:鸡肋。

先看一下代码实现,然后再看这个模式是如何让我对epoll模块化腐朽为神奇的!!!

示例代码

代码语言:javascript复制
#include<iostream>

using namespace std;

//抽象任务类
class abstractRW {
public:
	virtual int get_diff() = 0;	//获取任务等级
	virtual string get_RW() = 0;	//获取任务内容
};

//具体任务类
class RW:public abstractRW{
protected:
	int diff = 0;
	string str = " ";
public:
	RW(int diff, string str) {
		this->diff = diff;
		this->str = str;
	}
	int get_diff() { return this->diff; }	//设置任务等级
	string get_RW() { return this->str; }	//设置任务内容
};

//任务分配类
class handle {
private:
	int level = 5;
	handle* nexthandle;
protected:
	virtual void respon(abstractRW* rw) = 0;
public:
	handle(int l) { this->level = l; }
	void handleMessage(abstractRW* rw) {
		if (rw->get_diff() == this->level)
			this->respon(rw);
		else
		{
			if (this->nexthandle != NULL)
				this->nexthandle->handleMessage(rw);
			else
				cout << "没人接" << endl;
		}
	}
	void setNext(handle* h) { this->nexthandle = h; }
};

class P6 :public handle {
public:
	P6(int lev) :handle(lev){}	//继承写法

	void respon(abstractRW* rw) {
		cout << rw->get_RW() << " P6接了" << endl;
	}
};

class P7 :public handle {
public:
	P7(int lev) :handle(lev) {}

	void respon(abstractRW* rw) {
		cout << rw->get_RW() << " P7接了" << endl;
	}
};

class P8 :public handle {
public:
	void respon(abstractRW* rw) {
		cout << rw->get_RW() << " P8接了" << endl;
	}
};

int main()
{
	abstractRW* rw = new RW(7,"没事儿");
	handle* h6 = new P6(6);
	handle* h7 = new P7(7);

	h6->setNext(h7);

	h6->handleMessage(rw);
	return 0;
}

第一个epoll模块

我第一次接触epoll,是在培训班的项目中。那也是我写的第一个Linux网络编程相关的项目。

但是只有一个念想:把老师给的案例吃透,仿写,能动。

时间紧任务重,我的手上还带着团队呢,我要让我的团队,快速运转。

旧的类图是这样的:

类图里少画了一个SOCK对象,和一个心跳模块的对象。

可以看出,这个epoll类完全成为了前置服务器的中枢神经,负重前行,前置服务器中所有的对象它都要管。

但是,它也只是个功能模块啊,怎么忍心让它负担这么重???


使用责任链模式优化过的epoll模块

秉着“单一职责原则”,我认为epoll只需要且只能监听文件描述符,但是它不应该知道消息内容,更不应该对消息进行处理。

这个问题确实也困扰了我,我想了好久,因为我以前的做法都是epoll收到消息后,判断是哪个地方来的消息,如果是监听套接字,则判定是有新连接上来,处理连接(这里就需要将网络连接模块和epoll模块放在一起,这是其一);如果是通信套接字(客户端)来的消息,那么就是客户端有消息上来,还要判断是否空包(空包为客户端掉线,需要处理),若不是空包,则对包进行一个基本的判断(这里就需要解压包模块的介入,这是其二),之后将包发往中控服务器(这里就需要进程间通信模块的介入,这是其三);对包的处理与转发还使用了小型线程池(这就需要线程池模块的参与,这是其四),此外,还有日志模块和心跳检测模块,这么多东西,如今一锅炖在epoll模型中,成何体统?

但是又有什么办法呢?请求来了,自然是要回应的啊,要回应,就需要各个模块之间的配合了,我思来想去,想到了责任链模式。

我以前一直觉得这个模式简直是鸡肋,但是这次之后我改观了,没有鸡肋的设计模式,只有鸡肋的设计师。设计模式的优势是什么?

  • 将请求和处理分开。
  • 请求者可以不知道是谁处理了,处理者也不用知道请求者的全貌。
  • 两者解耦,提高系统的灵活性。

于是便有了以下这张图,也正是这张图吸引了听我讲这个项目设计的朋友们:

现在epoll就可以专心干自己的事情了。

具体实现以及对图的释义,我当时有日报纪录:FTP文件管理项目(本地云)项目日报(六)


0 人点赞