设计模式笔记(十)– 责任链模式

​责任链模式(chain of responsibility pattern)用于对请求对象进行流水线的处理。


问题提出

来看一实际的例子。

WEB服务器类WebServer,有一个方法handle。handle方法中,依次对Http请求进行身份验证authentication、记录日志logging、压缩compression等操作任务。

我们不想在handle方法中实现所有这些操作任务,因为这违反了单一职责原则,我们将每一个操作任务放在一个独立的类中。

创建Authenticator类,该类有authenticate方法,用于验证发送请求的用户是否合法。

创建Compressor类,模拟压缩任务。

创建Logger类,模拟记录日志。    

现在,回到WebServer类调用相关操作任务。

比如新建Authenticator对象,调用authenticate方法;接着创建Logger、Compressor等对象并调用相应的方法。显然,WebServer类与这些对象的耦合过于紧密。

可以将Authenticator、Logger和Compressor类分别提取出接口,并让WebServer仅依赖于提取的接口。

但依然有问题。

各个操作任务的执行顺序是固定的,先执行Authenticate,再执行compress,接着执行log。如果后面我们需要禁止执行log,就得回到WebServer中去修改。如果需要添加某个操作任务比如加密encryption,我们也得回去修改。

这就是责任链模式需要解决的问题。

责任链模式不仅仅用于处理Http请求,它可以应用在需要流水线处理任何请求的场景。


解决方案

一种方法,就是提取出Authenticator接口、Compressor接口,Logger接口,WebServer依赖这些接口,在handle方法里,依次调用接口定义的方法。这种方法将WebServer与具体的Authenticator、Compressor、Logger实现类解耦,符合开放闭合原则,也就是对扩展开放,而对修改闭合。

但正如前面我们提到过的,这种方案的问题是:在禁用或添加某个操作任务时,需要回头修改WebServer的handle方法。

真实的场景经常是这样:我们需要对请求对象进行流水线操作或者叫链接式操作,某个节点处理完了,交给下一个节点处理。比如我们首先将对象交给Authenticator节点处理,Authenticator验证用户是合法的,然后交给下一个节点,不合法就中止。

由此可见,每一个处理节点只需知道下一个节点即可。

我们提取抽象类Handler类,它有属性next,代表任务链的下一个Handler节点,Handler定义一个handle()方法,由具体的Handler类实现。

对于WebServer而言,它只需知道第1个节点,并且只需是Handler接口而不是具体的实现,这样就与具体的操作任务解耦。具体的操作任务如Authenticator、Logger实现Handler的handle方法。

这个就是责任链模式(Chain of Responsibility Pattern)。


代码实现

创建抽象类Handler,添加属性next用于指向下一个Handler。利用模板方法模式创建handle方法和doHandle方法。handle方法定义执行的结构:判断doHandle的执行结果,如果是true表示中止流水线,如果是false则在下一个节点不为空的情况下继续执行。

修改Authentiation类使其继承于Handler,实现doHandle方法。

同样的,修改Compressor和Logger。

再到WebServer类,添加handler属性,该属性指向流水线的第一个节点。修改handle的实现:调用handler的handle方法。

最后到main方法测试。

假设我们希望执行任务的顺序是authentication->log->compress,那么给Compressor构造函数传递的next属性是null,Logger的next属性是compressor,Authenticator的next属性则是logger。

如果想禁用某个任务比如logger也非常简单,只要在main中稍作调整即可,不必修改WebServer类。

在流水线中添加操作任务也很简单。

比如我们在Compressor后增加一个操作Encryption。先创建Encryption类。

然后在main中调整链接列表顺序。


小结

责任链模式用于对请求对象进行流水线处理的场景。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注