设计模式之责任链模式

1.1 责任链模式定义

  • 责任链模式为请求创建一个接收者对象链,每个接收者都包含对另一个接收者的引用,如果一个对象不能处理该请求,那么它会把请求传给下一个接收者,依此类推
  • 责任链模式避免了请求的发送者和接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连成一条链,并且沿着这条链传递请求,直到有对象处理它为止。

1.2 责任链模式优缺点

 优点

降低耦合度。它将请求的发送者和接收者解耦 
简化了对象,使得对象不需要知道链的结构 
增强给对象指派职责的灵活性,允许动态地新增或者删除责任链 
增加新的请求处理类方便

 缺点

不能保证请求一定被接收; 
系统性能将受到一定影响,调试时不方便,可能会造成循环调用

2 模式结构

 2.1 组成

  Handler(抽象处理者) : 定义一个处理请求的接口,提供对后续处理者的引用 
  ConcreteHandler(具体处理者) : 抽象处理者的子类,处理用户请求,可选将请求处理掉还是传给下家;在具体处理者中可以访问链中下一个对象,以便请求的转发

 2.2 类图及设计

代码详解:

 抽象处理者

public abstract class Handler {

    protected Handler nextHandler; // 下一个责任链成员

    public Handler getNextHandler() {
        return nextHandler;
    }

    public void setNextHandler(Handler nextHandler) {
        this.nextHandler = nextHandler;
    }

    // 处理传递过来的时间
    public abstract void handleMessage(int type);
}

 具体处理者 
  在当前处理者对象无法处理时,将执行权传给下一个处理者对象

public class ConcreteHandler1 extends Handler {

    @Override
    public void handleMessage(int type) {
        if (type == 1 || type == 3) {
            System.out.println("ConcreteHandler1解决了问题!");
        } else {
            System.out.println("ConcreteHandler1解决不了问题......");
            if (nextHandler != null) {
                nextHandler.handleMessage(type);
            } else {
                System.out.println("没有人能处理这个消息");
            }
        }
    }
}

public class ConcreteHandler2 extends Handler {

    @Override
    public void handleMessage(int type) {
        if (type == 2 || type == 5) {
            System.out.println("ConcreteHandler2解决了问题!");
        } else {
            System.out.println("ConcreteHandler2解决不了问题......");
            if (nextHandler != null) {
                nextHandler.handleMessage(type);
            } else {
                System.out.println("没有人能处理这个消息");
            }
        }
    }
}

public class ConcreteHandler3 extends Handler {

    @Override
    public void handleMessage(int type) {
        if (type == 4 || type == 6) {
            System.out.println("ConcreteHandler3解决了问题!");
        } else {
            System.out.println("ConcreteHandler3解决不了问题......");
            if (nextHandler != null) {
                nextHandler.handleMessage(type);
            } else {
                System.out.println("没有人能处理这个消息");
            }
        }
    }
}

 Client 客户端调用

        // 初始化责任链:handler1 -> handler2 -> handler3
        Handler handler1 = new ConcreteHandler1();
        Handler handler2 = new ConcreteHandler2();
        Handler handler3 = new ConcreteHandler3();
        handler2.setNextHandler(handler3);
        handler1.setNextHandler(handler2);
        // 处理事件
        System.out.println("--------------Message1");
        handler1.handleMessage(1);
        System.out.println("--------------Message2");
        handler1.handleMessage(2);
        System.out.println("--------------Message3");
        handler1.handleMessage(4);
        System.out.println("--------------Message4");
        handler1.handleMessage(7);    

  从上述模式可以知道,当我们需要多个 if else 做逻辑判断的时候,可以引入,从而提高代码可维护性

 2.3 适用场景:

  • 有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定
  • 在不明确指定接收者的情况下,向多个对象中的某一个对象提交一个请求
  • 可动态指定一组对象的处理请求

  关于责任链模式,其有两种形式,一种是通过外部调用的方式对链的各个节点调用进行控制,从而进行链的各个节点之间的切换;另一种是链的每个节点自由控制是否继续往下传递链的进度,这种比较典型的使用方式就是Netty中的责任链模式。本文主要讲解我们如何在Spring中使用这两种责任链模式。

1. 外部控制模式

       对于外部控制的方式,这种方式比较简单,链的每个节点只需要专注于各自的逻辑即可,而当前节点调用完成之后是否继续调用下一个节点,这个则由外部控制逻辑进行。这里我们以一个过滤器的实现逻辑为例进行讲解,在平常工作中,我们经常需要根据一系列的条件对某个东西进行过滤,比如任务服务的设计,在执行某个任务时,其需要经过诸如时效性检验,风控拦截,任务完成次数等过滤条件的检验之后才能判断当前任务是否能够执行,只有在所有的过滤条件都完成之后,我们才能执行该任务。那么这里我们就可以抽象出一个Filter接口,其设计如下:

 这里的Filter.filter()方法只有一个参数Task,主要就是控制当前task是否需要被过滤掉,其有一个boolean类型的返回值,通过该返回值以告知外部控制逻辑是否需要将该task过滤掉。对于该接口的子类,我们只需要将其声明为Spring所管理的一个bean即可:

 

 

 上面我们模拟声明了三个Filter的子类,用于设计一系列的控制当前task是否需要被过滤的逻辑,结构上的逻辑其实比较简单,主要就是需要将其声明为Spring所管理的一个bean。下面是我们的控制逻辑:

  在上述的控制逻辑中,对于过滤器的获取,只需要通过Spring的自动注入即可,这里注入的是一个List<Filter>,也就是说,如果我们有新的Filter实例需要参与责任链的过滤,只需要将其声明为一个Spring容器所管理的bean即可。

       这种责任链设计方式的优点在于链的控制比较简单,只需要实现一个统一的接口即可,其基本上能够满足大部分的逻辑控制,但是对于某些需要动态调整链的需求其就无能为力了。比如在执行到某个节点之后需要动态的判断是否执行下一个节点,或者说要执行某些分叉的节点等等。这个时候我们就需要将链节点的传递工作交由各个节点进行。

2. 节点控制模式(netty的实现方式)

       对于节点控制调用的方式,其主要有三个控制点:Handler,HandlerContext和Pipeline。Handler中是用于编写具体的业务代码的;HandlerContext则主要是用于对Handler进行包裹,并且用于控制进行下一个节点的调用的;Pipeline则主要是用于控制整体的流程调用的,比如对于任务的执行,其有任务的查询,任务的过滤和执行任务等等流程,这些流程整体的逻辑控制就是由Pipeline来控制的,在每个流程中又包含了一系列的子流程,这些子流程则是由一个个的HandlerContext和Handler进行梳理的。

代码下次分享

 

1. 外部控制模式

       对于外部控制的方式,这种方式比较简单,链的每个节点只需要专注于各自的逻辑即可,而当前节点调用完成之后是否继续调用下一个节点,这个则由外部控制逻辑进行。这里我们以一个过滤器的实现逻辑为例进行讲解,在平常工作中,我们经常需要根据一系列的条件对某个东西进行过滤,比如任务服务的设计,在执行某个任务时,其需要经过诸如时效性检验,风控拦截,任务完成次数等过滤条件的检验之后才能判断当前任务是否能够执行,只有在所有的过滤条件都完成之后,我们才能执行该任务。那么这里我们就可以抽象出一个Filter接口,其设计如下:

  1.  
    public interface Filter {
  2.  
     
  3.  
    /**
  4.  
    * 用于对各个任务节点进行过滤
  5.  
    */
  6.  
    boolean filter(Task task);
  7.  
     
  8.  
    }

        这里的Filter.filter()方法只有一个参数Task,主要就是控制当前task是否需要被过滤掉,其有一个boolean类型的返回值,通过该返回值以告知外部控制逻辑是否需要将该task过滤掉。对于该接口的子类,我们只需要将其声明为Spring所管理的一个bean即可:

  1.  
    // 时效性检验
  2.  
    @Component
  3.  
    public class DurationFilter implements Filter {
  4.  
     
  5.  
    @Override
  6.  
    public boolean filter(Task task) {
  7.  
    System.out.println("时效性检验");
  8.  
    return true;
  9.  
    }
  10.  
    }
  1.  
    // 风控拦截
  2.  
    @Component
  3.  
    public class RiskFilter implements Filter {
  4.  
     
  5.  
    @Override
  6.  
    public boolean filter(Task task) {
  7.  
    System.out.println("风控拦截");
  8.  
    return true;
  9.  
    }
  10.  
    }
  1.  
    // 次数限制校验
  2.  
    @Component
  3.  
    public class TimesFilter implements Filter {
  4.  
     
  5.  
    @Override
  6.  
    public boolean filter(Task task) {
  7.  
    System.out.println("次数限制检验");
  8.  
    return true;
  9.  
    }
  10.  
    }

       上面我们模拟声明了三个Filter的子类,用于设计一系列的控制当前task是否需要被过滤的逻辑,结构上的逻辑其实比较简单,主要就是需要将其声明为Spring所管理的一个bean。下面是我们的控制逻辑:

  1.  
    @Service
  2.  
    public class ApplicationService {
  3.  
     
  4.  
    @Autowired
  5.  
    private List<Filter> filters;
  6.  
     
  7.  
    public void mockedClient() {
  8.  
    Task task = new Task(); // 这里task一般是通过数据库查询得到的
  9.  
    for (Filter filter : filters) {
  10.  
    if (!filter.filter(task)) {
  11.  
    return;
  12.  
    }
  13.  
    }
  14.  
     
  15.  
    // 过滤完成,后续是执行任务的逻辑
  16.  
    }
  17.  
    }

       在上述的控制逻辑中,对于过滤器的获取,只需要通过Spring的自动注入即可,这里注入的是一个List<Filter>,也就是说,如果我们有新的Filter实例需要参与责任链的过滤,只需要将其声明为一个Spring容器所管理的bean即可。

       这种责任链设计方式的优点在于链的控制比较简单,只需要实现一个统一的接口即可,其基本上能够满足大部分的逻辑控制,但是对于某些需要动态调整链的需求其就无能为力了。比如在执行到某个节点之后需要动态的判断是否执行下一个节点,或者说要执行某些分叉的节点等等。这个时候我们就需要将链节点的传递工作交由各个节点进行。

原文地址:https://www.cnblogs.com/lq-93/p/13900089.html