写点什么

职责链模式

用户头像
soolaugust
关注
发布于: 2020 年 12 月 26 日
职责链模式

在之前的观察者模式实战中,我们说过并不是所有的状态监控场景都适用于观察者模式,比如最近在看的Android项目中,当一个页面的组件很多时,对同一个时间的响应上,Android设计了一种传递机制,当我们合理利用后可以让我们想要的组件来响应事件,而如果是观察者模式,那么所有的组件都会响应。所以我们今天就来看这一种新的设计模式--职责链模式,也称为责任链模式。



场景分析

那么现在让我们来看一下这样一种需求:在Android或者类前端中,除了要处理很多事件之外,还有很多的情况是UI组件的重叠,在设计上就可能导致很多问题:

  • 多重响应:如果重叠的每一个组件都订阅了触摸事件,在不进行其他的限制的情况下,当用户触摸时,就会出现每个UI组件都会响应事件,这个在大部分情况都是不允许的

  • 顶部响应:由于UI组件的重叠,上层处理事件后如果就结束了,很有可能导致下面的组件没有办法收到事件而无法响应

  • 不可控:由于组件都是事件的订阅者,地位相同,在收到事件后分别作出反应。而事件的接受顺序是不可控的,这就导致组件可能不是按照预想的方式进行下去。并且如果我们想在事件到达某一层后停止也没有办法做到。



那么我们可以看到,在这种情况下,仅仅使用观察者模式并不能解决问题,我们更需要关注的是事件的传递。那么我们来看一下职责链模式是如何解决这个问题的。



职责链模式

我们在观察者模式中提到的设计模式图中,就描绘过职责链模式也属于行为型设计模式,如果不了解行为型模式的可以参考之前的文章,按照设计模式的分析顺序,我们来看一下职责链模式的意图、动机、结构和代码实现。



演示文稿1.jpg



意图

职责链模式是这样一种设计模式,设计一种处理链,允许请求沿着处理链进行发送。收到请求后,每个处理者都可以对请求进行处理,并选择是否将请求传递给下一个处理者,或者封装新的请求传递下去。



演示文稿1.jpg



动机

我们在上面描述的组件响应事件就是职责链应用的典型场景。在我们构造UI组件时,一般来说都是都是一种树形结构,那么事件的传递其实就是从叶子到根的一条链路,如下图:



image



由于UI的树形结构,我们很容易根据需求设计出职责链模式。但是在其他不明显的业务上,就不一定第一时间想到职责链模式。我们来分析这样一个需求,那就是在web服务里面增加用户验证。



一般来说,我们很容易想到定义一个验证方法,然后接受用户的登录请求,然后进行验证。这个并不难,但是这时需求增加了一项,需要先去另一个系统认证,如果验证成功了,再进行加下来的验证。这个也不难,我们可以在验证方法中加入这个逻辑。系统用了一段时间后,发现验证方法太弱了,需要增加额外的检查,这时我们只能继续在验证方法中添加,然后后面发现验证的效率不够高,需要增加缓存来提高响应速度。这时我们还是会想到直接在验证方法里面加入逻辑即可。



image



这时我们就发现这个设计有问题了,因为随着需求的不断变更,我们的验证方法变得越来越复杂,即使我们将代码拆分,也会让其他人阅读和维护变得困难。更重要的是,当我们打算复用这段代码的时候,我们必须再按照需求去一一检查我们的逻辑,将可用的代码复制过去,而且如果不是我们自己写的,我们很有可能会被其中的逻辑依赖搞晕。



而这就是设计模式的用途了,代码随着不断发展问题变得越来越多,说明我们之前的设计就有问题。那我们尝试用上面的职责链模式来重构我们的这部分逻辑:

  • 首先定义一条链路,也就是将检查步骤的逻辑抽象成相同的结构,然后将步骤注册到链路中。

  • 然后定义每一步的参数是请求数据,每个处理者处理完后都会返回一个状态来确定是继续处理还是终止。

这样的话,就变成了下面的结构:



image



那么我们看一下有没有解决上面的问题,第一就是需求不断变更,在这种情况下我们只需要增加一个步骤并将其注册到链路中,而整个链路的逻辑不需要任何的变化。然后就是复用,我们可以直接将链路复制过去,然后将我们需要的步骤移过去,由于每个步骤是完全对等的,所以就不存在什么逻辑依赖了。



结构



经过上面的分析,我们就很容易得到职责链的数据结构:

  • 处理者:也就是处理请求的具体逻辑,并且返回值需要能够决定请求是否继续传递。



image.png



然后就是我们的客户端负责生成链路并注册处理者。整个职责链模式的结构非常简单,重要的是这个设计理念。



代码实现



职责链模式在很多语言和框架设计上都有所体现,比如UI组件设计,过滤器等。这里我们就简单实现一下职责链模式(使用的是Java,其他语言可自行设计):



  • 首先检查业务逻辑,是否可以将请求处理逻辑拆分成相同结构的方式

  • 定义处理者接口

// 处理者接口
public interface HandlerInterface {
// 处理函数
// true表示已处理完,不需要继续处理
// false表示传递给下一个处理者
boolean handle(Object request);
}
  • 定义两种处理者,一种处理完继续传递,一种处理完结束。

// 处理完后终止传递
public class BlockHandler implements HandlerInterface {
@Override
public boolean handle(Object request) {
System.out.printf("request %s is received, block the chain\n", request.toString());
return true;
}
}

// 处理完传递给下一个处理者
public class PassThroughHandler implements HandlerInterface {
@Override
public boolean handle(Object request) {
System.out.printf("request %s is received, continue handling\n", request.toString());
return false;
}
}

  • 定义客户端,组装职责链并处理请求

public class Application {
// 职责链
private static List<HandlerInterface> mHandlerChain = new ArrayList<>();

public static void main(String[] args){
// 处理链条是: * -> x -> *
// 第三个不会达到
HandlerInterface passThroughHandler = new PassThroughHandler();
mHandlerChain.add(passThroughHandler);

HandlerInterface blockHandler = new BlockHandler();
mHandlerChain.add(blockHandler);

HandlerInterface unreachableThroughHandler = new PassThroughHandler();
mHandlerChain.add(unreachableThroughHandler);

String request = "mock-request";

for(HandlerInterface handler : mHandlerChain){
if (handler.handle(request)){
return;
}
}
}
}

// 输出:
// request mock-request is received, continue handling
// request mock-request is received, block the chain

代码见:https://github.com/soolaugust/design-pattern-demo/tree/master/src/chain/of/responsibility

总结

这一篇我们对职责链模式有了基本的了解,能够在遇到相似的场景时,考虑是否使用职责链模式。在没有真实的项目经验下,了解这些就可以了。



在下一篇我们会进一步讲解职责链模式的内容,并讲解如何来使用职责链模式。



发布于: 2020 年 12 月 26 日阅读数: 400
用户头像

soolaugust

关注

公众号:雨夜随笔 2018.09.21 加入

公众号:雨夜随笔

评论

发布
暂无评论
职责链模式