您现在的位置是:首页 >技术杂谈 >设计模式之【责任链模式】,路铺好了,你走不走?网站首页技术杂谈

设计模式之【责任链模式】,路铺好了,你走不走?

秃了也弱了。 2024-06-20 18:01:02
简介设计模式之【责任链模式】,路铺好了,你走不走?

一、什么是责任链模式

责任链模式(Chain of Responsibility Patter)也称职责链模式,是将链中每一个节点看做是一个对象,每个节点处理的请求均不同,且内部自动维护一个下一节点对象。当一个请求从链式的首端发出时,会沿着链的路径依次传递给每一个节点对象,直至有对象处理这个请求为止。责任链模式属于行为型模式。

1、状态模式与责任链模式的区别

状态模式和责任链模式都能消除if分支过多的问题。但某些情况下,状态模式中的状态可以理解为责任,那么这种情况下,两种模式都可以使用。

从定义来看,状态模式强调的是一个对象内在状态的改变,而责任链模式强调的是外部节点对象间的改变。

从其代码实现上来看,他们间最大的区别就是状态模式各个状态对象知道自己下一个要进入的状态对象;而责任链模式并不清楚其下一个节点处理对象,因为链式组装由客户端负责。

2、责任链模式使用场景

在日常生活中责任链模式还是比较常见的,我们平时处理一些工作,往往是各部分协同合作完成的某一个任务。每个部门都有各自的职责,因此很多时候事情做一半,便会转交给下一个部门;再比如公司员工请假,可批假的领导有部门负责人、副总经理、总经理等,但每个领导能批准的天数不同,员工必须根据自己要请假的天数去找不同的领导签名。

责任链模式主要是解耦了请求与处理,客户端只需要将请求发送到链上即可,无需关心请求的具体内容和处理细节,请求会自动进行传递直至有节点对象进行处理。适用于以下应用场景:

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

3、责任链模式的优缺点

优点:

  • 降低了对象之间的耦合度:该模式降低了请求发送者和接收者的耦合度。
  • 增强了系统的可扩展性:可以根据需要增加新的请求处理类,满足开闭原则。
  • 增强了给对象指派职责的灵活性:当工作流程发生变化,可以动态地改变链内的成员或者修改它们的次序,也可动态地新增或者删除责任。
  • 责任链简化了对象之间的连接:一个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
  • 责任分担:每个类只需要处理自己该处理的工作,不能处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。

缺点:

  • 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
  • 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
  • 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。

4、责任链模式的角色

在这里插入图片描述
职责链模式主要包含以下角色:

  • 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
  • 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
  • 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。

责任链模式的本质是解耦请求与处理,让请求在处理链中能进行传递与被处理;理解责任链模式应当理解的是其模式(道)而不是其具体实现(术),责任链模式的读到支出是其将节点处理者组合成了链式结构,并允许节点自身决定是否进行请求处理或转发,相当于让请求流动了起来。

二、实例

1、责任链模式的一般写法

责任链模式一般来说有三种写法,一种是传统方式在自身内部定义下一个处理者;还有一种是单独定义一条链,用来关联首尾;还有就是单独定义一条链,使用List进行存放处理者。

这三种写法的效果是一样的,任何一种写法都可以使用其他写法完美平替,使用起来没有任何区别,可以根据场景进行判断。

(1)一般写法

// 抽象处理者
public abstract class Handler {

    protected Handler nextHandler;

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

    public abstract void handleRequest(String request);

}
// 具体处理者
public class ConcreteHandlerA extends Handler {

    public void handleRequest(String request) {
        if ("requestA".equals(request)) {
            System.out.println(this.getClass().getSimpleName() + "deal with request: " + request);
            return;
        }
        if (this.nextHandler != null) {
            this.nextHandler.handleRequest(request);
        }
    }
}
public class ConcreteHandlerB extends Handler {

    public void handleRequest(String request) {
        if ("requestB".equals(request)) {
            System.out.println(this.getClass().getSimpleName() + "deal with request: " + request);
            return;
        }
        if (this.nextHandler != null) {
            this.nextHandler.handleRequest(request);
        }
    }
}
// 测试类
public class Test {
    public static void main(String[] args) {
        Handler handlerA = new ConcreteHandlerA();
        Handler handlerB = new ConcreteHandlerB();
        handlerA.setNextHanlder(handlerB);
        handlerA.handleRequest("requestB");
    }
}

(2)一般写法-使用建造者模式进阶

上面的Handler加上建造者:

public abstract class Handler {

    protected Handler nextHandler;

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

    public abstract void handleRequest(String request);

    public static class Builder{
        private Handler head;
        private Handler tail;

        public Builder addHandler(Handler handler){
            if (this.head == null) {
                this.head = this.tail = handler;
                return this;
            }
            this.tail.setNextHanlder(handler);
            this.tail = handler;
            return this;
        }

        public Handler build(){
            return this.head;
        }

    }
}
// 测试类
public class Test {
    public static void main(String[] args) {
        Handler.Builder builder = new Handler.Builder();
        builder.addHandler(new ConcreteHandlerA())
                .addHandler(new ConcreteHandlerB())
                .build().handleRequest("requestB");
    }
}

(3)进阶写法①

将Handler使用HandlerChain 统一管理

public abstract class Handler {
  protected Handler successor = null;
  public void setSuccessor(Handler successor) {
    this.successor = successor;
  }
  public final void handle() {
    boolean handled = doHandle();
    if (successor != null && !handled) {
      successor.handle();
    }
  }
  protected abstract boolean doHandle();
}
public class HandlerA extends Handler {
  @Override
  protected boolean doHandle() {
    boolean handled = false;
    //...
    return handled;
  }
}
public class HandlerB extends Handler {
  @Override
  protected boolean doHandle() {
    boolean handled = false;
    //...
    return handled;
  }
}
public class HandlerChain {
  private Handler head = null;
  private Handler tail = null;
  public void addHandler(Handler handler) {
    handler.setSuccessor(null);
    if (head == null) {
      head = handler;
      tail = handler;
      return;
    }
    tail.setSuccessor(handler);
    tail = handler;
  }
  public void handle() {
    if (head != null) {
      head.handle();
    }
  }
}
// 使用举例
public class Application {
  public static void main(String[] args) {
    HandlerChain chain = new HandlerChain();
    chain.addHandler(new HandlerA());
    chain.addHandler(new HandlerB());
    chain.handle();
  }
}

(4)进阶写法②

使用List管理Handler。

public interface IHandler {
  boolean handle();
}
public class HandlerA implements IHandler {
  @Override
  public boolean handle() {
    boolean handled = false;
    //...
    return handled;
  }
}
public class HandlerB implements IHandler {
  @Override
  public boolean handle() {
    boolean handled = false;
    //...
    return handled;
  }
}
public class HandlerChain {
  private List<IHandler> handlers = new ArrayList<>();
  public void addHandler(IHandler handler) {
    this.handlers.add(handler);
  }
  public void handle() {
    for (IHandler handler : handlers) {
      boolean handled = handler.handle();
      if (handled) {
        break;
      }
    }
  }
}
// 使用举例
public class Application {
  public static void main(String[] args) {
    HandlerChain chain = new HandlerChain();
    chain.addHandler(new HandlerA());
    chain.addHandler(new HandlerB());
    chain.handle();
  }
}

2、数据校验案例

来看一段我们经常会写到的代码:

public class MemberService {

    public void login(String loginName,String loginPass){
        if(StringUtils.isEmpty(loginName) ||
                StringUtils.isEmpty(loginPass)){
            System.out.println("用户名和密码为空");
            return;
        }
        System.out.println("用户名和密码不为空,可以往下执行");

        Member member = checkExists(loginName,loginPass);
        if(null == member){
            System.out.println("用户不存在");
            return;
        }
        System.out.println("登录成功!");

        if(!"管理员".equals(member.getRoleName())){
            System.out.println("您不是管理员,没有操作权限");
            return;
        }
        System.out.println("允许操作");

    }

    private Member checkExists(String loginName,String loginPass){
        Member member = new Member(loginName,loginPass);
        member.setRoleName("管理员");
        return member;
    }

    public static void main(String[] args) {
        MemberService service = new MemberService();
        service.login("tom","666");
    }

}

接下来我们使用责任链模式进行优化:

// 创建一个Handler
public abstract class Handler {
    protected Handler next;
    public void next(Handler next){ this.next = next;}

    public abstract void doHandler(Member member);
}
// 具体Handler
public class ValidateHandler extends Handler {
    public void doHandler(Member member) {
        if(StringUtils.isEmpty(member.getLoginName()) ||
                StringUtils.isEmpty(member.getLoginPass())){
            System.out.println("用户名和密码为空");
            return;
        }
        System.out.println("用户名和密码不为空,可以往下执行");
        if(null != next) {
            next.doHandler(member);
        }
    }
}
public class AuthHandler extends Handler {
    public void doHandler(Member member) {
        if(!"管理员".equals(member.getRoleName())){
            System.out.println("您不是管理员,没有操作权限");
            return;
        }
        System.out.println("允许操作");
    }
}
public class LoginHandler extends Handler {
    public void doHandler(Member member) {
        System.out.println("登录成功!");
        member.setRoleName("管理员");
        if(null != next) {
            next.doHandler(member);
        }
    }
}

public class MemberService {

    public void login(String loginName,String loginPass){
        Handler validateHandler = new ValidateHandler();
        Handler loginHandler = new LoginHandler();
        Handler authHandler = new AuthHandler();

        validateHandler.next(loginHandler);
        loginHandler.next(authHandler);

        validateHandler.doHandler(new Member(loginName,loginPass));

    }

}

// 测试类
public class Test {
    public static void main(String[] args) {
        MemberService memberService = new MemberService();
        memberService.login("tom","666");
    }
}

优化:引入建造者模式

因为责任链模式具备链式结构,而上面的代码中,我们看到,组装链式结构的角色是MemberService,当链式结构较长时,MemberService的工作会非常繁琐,并且MemberService代码相对臃肿,且后续 更改处理者或消息类型时,都必须在MemberService中进行修改,不符合开闭原则。产生这些问题的原因就是因为链式结构的组装过于复杂,而对于复杂结构的创建,很自然我们就会想到建造者模式,使用建造者模式,我们完全可以对MemberService指定的处理节点对象进行自动链式组装,客户端只需指定处理节点对象,其他任何事情都无需关心,并且客户指定的处理节点对象顺序不同,构造出来的链式结构也随之不同。

我们改造一下,修改Handler的代码:

public abstract class Handler {
    protected Handler next;
    // 私有化
    private void next(Handler next){ this.next = next;}

    public abstract void doHandler(Member member);

    public static class Builder{
        private Handler head;
        private Handler tail;

        public Builder addHandler(Handler handler){
                if (this.head == null) {
                    this.head = this.tail = handler;
                    return this;
                }
                this.tail.next(handler);
                this.tail = handler;
            return this;
        }

        public Handler build(){
            return this.head;
        }

    }
}

然后修改MemberService的代码:

public class MemberService {

    public void login(String loginName,String loginPass){

        Handler.Builder builder = new Handler.Builder();

        builder.addHandler(new ValidateHandler());
                .addHandler(new LoginHandler())
                .addHandler(new AuthHandler());

        builder.build().doHandler(new Member(loginName,loginPass));
        //用过Netty的人,肯定见过
    }
}

三、源码中的责任链模式

1、FilterChain

在javaWeb应用开发中,FilterChain是职责链(过滤器)模式的典型应用,以下是Filter的模拟实现分析:

// 模拟web请求Request以及web响应Response
public interface Request{
}
public interface Response{
}
// 模拟web过滤器Filter
public interface Filter {
	public void doFilter(Request req,Response res,FilterChain c);
}
// 模拟实现具体过滤器
public class FirstFilter implements Filter {
	@Override
	public void doFilter(Request request, Response response, FilterChain chain) {
		System.out.println("过滤器1 前置处理");
		// 先执行所有request再倒序执行所有response
		chain.doFilter(request, response);
		System.out.println("过滤器1 后置处理");
	}
}
public class SecondFilter implements Filter {
	@Override
	public void doFilter(Request request, Response response, FilterChain chain) {
		System.out.println("过滤器2 前置处理");
		// 先执行所有request再倒序执行所有response
		chain.doFilter(request, response);
		System.out.println("过滤器2 后置处理");
	}
}
// 模拟实现过滤器链FilterChain
public class FilterChain {
	private List<Filter> filters = new ArrayList<Filter>();
	private int index = 0;
	// 链式调用
	public FilterChain addFilter(Filter filter) {
		this.filters.add(filter);
		return this;
	}
	public void doFilter(Request request, Response response) {
		if (index == filters.size()) {
			return;
		}
		Filter filter = filters.get(index);
		index++;
		filter.doFilter(request, response, this);
	}
}
// 测试类
public class Client {
	public static void main(String[] args) {
		Request req = null;
		Response res = null ;
		FilterChain filterChain = new FilterChain();
		filterChain.addFilter(new FirstFilter()).addFilter(new
		SecondFilter());
		filterChain.doFilter(req,res);
	}
}

2、Netty

Netty中非常经典的串行化处理Pipeline就是采用了责任链设计模式。它底层采用双向链表的数据结构,将链上的各个处理器串联起来。客户端每一个请求的到来,Netty都认为Pipeline中的所有的处理器都有机会处理它。因此,对于入站的请求全部从头节点开始往后传播,一直传播到尾结点才会把消息释放掉。

更多Netty源码分析请移步:
Netty核心源码分析(三)业务请求执行关键——ChannelPipeline、ChannelHandler、ChannelHandlerContext源码分析

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。