一 行为型-职责链模式

410 阅读5分钟

本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。
小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

image.png 其他设计模式介绍
创建型: 工厂方法 抽象工厂 原型
结构型: 适配器 桥接模式 组合模式 装饰模式 外观模式 享元模式 代理模式
行为型: 职责链 命令 解释器 迭代器 中介者 备忘录 状态模式 策略模式 模板方法 访问者

定义

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

从文字角度出发,我们可以先将关注点放在“链”字上,很容易联想到链式结构,举个生活中常见的例子,击鼓传花游戏就是一个很典型的链式结构,所有人形成一条链,相互传递。而从另一个角度说,职责链就是所谓的多级结构,比如去医院开具病假条,普通医生只能开一天的证明,如果需要更多时常,则需将开具职责转交到上级去,上级医师只能开三天证明,如需更多时常,则需将职责转交到他的上级,以此类推,这就是一个职责链模式的典型应用。再比如公司请假,根据请假时常的不同,需要递交到的级别也不同,这种层级递进的关系就是一种多级结构。

UML

image.png

其中,Handler是抽象处理者,定义了一个处理请求的接口;ConcreteHandler是具体处理者,处理它所负责的请求,可访问它的后继者,如果可处理该请求就处理,否则就将该请求转发给它的后继者。

1. 抽象处理者

  抽象处理者实现了三个职责:

  • 定义一个请求的处理方法handlerMessage(),是唯一对外开放的方法
  • 定义一个链的编排方式setNext(),用于设置下一个处理者
  • 定义了具体的请求者必须实现的两个方法,即定义自己能够处理的级别的getHandlerLevel()方法及具体的处理任务echo()方法
 public abstract class Handler {

    private Handler nextHandler;    //下一个处理者

    public final Response handlerMessage(Request request) {
        Response response = null;

        if(this.getHandlerLevel().equals(request.getRequestLevel())) {    //判断是否是自己的处理级别
            response = this.echo(request);
        } else {
            if(this.nextHandler != null) {    //下一处理者不为空
                response = this.nextHandler.handlerMessage(request);
            } else {
                //没有适当的处理者,业务自行处理
            }
        }

        return response;
    }

    //设定下一个处理者
    public void setNext(Handler handler) {
        this.nextHandler = handler;
    }

    //每个处理者的处理等级
    protected abstract Level getHandlerLevel();

    //每个处理者都必须实现的处理任务
    protected abstract Response echo(Request request);

}

2. 具体处理者

  这里我们定义三个具体处理者,以便能组成一条链,ConcreteHandlerB及ConcreteHandlerC就不再赘述了。

public class ConcreteHandlerA extends Handler {

  @Override
  protected Level getHandlerLevel() {
      //设置自己的处理级别
      return null;
  }

  @Override
  protected Response echo(Request request) {
      //完成处理逻辑
      return null;
  }

}

3. Level类

  Level类负责定义请求和处理级别,具体内容需根据业务产生。

public class Level {
    //定义一个请求和处理等级
 }

4. Request类

  Request类负责封装请求,具体内容需根据业务产生。

public class Request {

  //请求的等级
  public Level getRequestLevel() {
      return null;
  }

}

5. Response类

  Response类负责封装链中返回的结果,具体内容需根据业务产生。

public class Response {
  //处理者返回的数据
}

6. Client客户端

  我们在场景类或高层模块中对类进行组装,并传递请求,返回结果。如下对三个具体处理者进行组装,按照1→2→3的顺序,并得出返回结果。

public class Client {

  public static void main(String[] args) {
      Handler handler1 = new ConcreteHandlerA();
      Handler handler2 = new ConcreteHandlerB();
      Handler handler3 = new ConcreteHandlerC();

      //设置链中的阶段顺序 1->2->3
      handler1.setNext(handler2);
      handler2.setNext(handler3);

      //提交请求返回结果
      Response response = handler1.handlerMessage(new Request());
  }

}

当然这是个未完成的模板,最终结果会因为 request.getRequestLevel() 为空而抛出异常,具体内容需根据业务逻辑进行编写。

职责链模式的应用

1. 何时使用

  • 处理消息时

2. 方法

  • 拦截的类都实现同一接口

3. 优点

  • 将请求和处理分开,实现解耦,提高系统的灵活性
  • 简化了对象,使对象不需要知道链的结构

4. 缺点

  • 性能会收到影响,特别是在链比较长的时候
  • 调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂
  • 不能保证请求一定被接收

5. 使用场景

  • 有多个对象可以处理同一个请求
  • 在不明确指定接收者的情况下,向多个对象中的提交请求
  • 可动态指定一组对象处理请求

6. 应用实例

  • 多级请求
  • 击鼓传花
  • 请假/加薪请求
  • Java Web中Tomcat对Encoding的处理、拦截器

7. 注意事项

  • 需控制链中最大节点数量,一般通过在Handler中设置一个最大节点数量,在setNext()方法中判断是否已经超过阀值,超过则不允许该链建立,避免出现超长链无意识地破坏系统性能