设计模式---责任链模式

一.概述

  1. 职责链模式(Chain of Responsibility Pattern), 又叫 责任链模式,为请求创建了一个接收者对象的链。这种模式对请求的发送者和接收者进行解耦。
  2. 职责链模式通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
  3. 这种类型的设计模式属于行为型模式
  • 原理类图:
    在这里插入图片描述
    职责链模式(Chain Of Responsibility),使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
  1. Handler : 抽象的处理者, 定义了一个处理请求的接口, 同时持有另外Handler
  2. ConcreteHandlerA , B 是具体的处理者, 处理它自己负责的请求, 可以访问它的后继者(即下一个处理者), 如果可以处理当前请求,则处理,否则就将该请求交个 后继者去处理,从而形成一个职责链
  3. Request , 含义很多属性,表示一个请求

二.如何实现责任链模式

/**
 * Created by John on 2018/11/22.
 * 价格处理人,负责处理客户折扣申请
 */
public abstract class PriceHandler {
    //直接后继处理人,用于传递请求
    protected PriceHandler successor;

    public void setSuccessor(PriceHandler successor) {
        this.successor = successor;
    }

    /**
     * 处理折扣申请
     * @param discount
     */
    public abstract void processDiscount(float discount);
}

/**
 * Created by John on 2018/11/22.
 * 销售, 可以批准5%以内的折扣
 */
public class Sales extends PriceHandler {

    @Override
    public void processDiscount(float discount) {
        if (discount <= 0.05){
            System.out.format("%s批准了折扣:%.2f%n",this.getClass().getName(),discount);
        }else {
            successor.processDiscount(discount);
        }
    }
}

/**
 * Created by John on 2018/11/22.
 * 销售小组长, 可以批准15%以内的折扣
 */
public class Lead extends PriceHandler {

    @Override
    public void processDiscount(float discount) {
        if (discount <= 0.15) {
            System.out.format("%s批准了折扣:%.2f%n", this.getClass().getName(), discount);
        } else {
            successor.processDiscount(discount);
        }
    }
}

/**
 * 销售经理, 可以批准30%以内的折扣
 */
public class Manager extends PriceHandler {
    @Override
    public void processDiscount(float discount) {
        if(discount<=0.3){
            System.out.format("%s批准了折扣:%.2f%n",this.getClass().getName(),discount);
        }else{
            System.out.format("%s拒绝了折扣:%.2f%n", this.getClass().getName(),discount);
        }
    }
}

public class PriceHandlerFactory {
    /**
     * 创建PriceHandler的工厂方法
     */
    public static PriceHandler createPriceHandler() {
        PriceHandler sales = new Sales();
        PriceHandler lead = new Lead();
        PriceHandler man = new Manager();
        sales.setSuccessor(lead);
        lead.setSuccessor(man);
        return sales;
    }
}

/**
 * 客户,请求折扣
 */
public class Customer {

    private PriceHandler priceHandler;

    public void setPriceHandler(PriceHandler priceHandler) {
        this.priceHandler = priceHandler;
    }

    public void requestDiscount(float discount) {
        priceHandler.processDiscount(discount);
    }

    public static void main(String[] args) {
        Customer customer = new Customer();
        customer.setPriceHandler(PriceHandlerFactory.createPriceHandler());
        Random rand = new Random();
        for (int i = 1; i <= 100; i++) {
            System.out.print(i + ":");
            customer.requestDiscount(rand.nextFloat());
        }
    }
}
  • 职责链模式的注意事项和细节
  1. 将请求和处理分开,实现解耦,提高系统的灵活性
  2. 简化了对象,使对象不需要知道链的结构
  3. 性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,一般
    通过在Handler中设置一个最大节点数量,在setNext()方法中判断是否已经超过阀值,超过则不允许该链建立,避免出现超长链无意识地破坏系统性能
  4. 调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂
  5. 最佳应用场景:有多个对象可以处理同一个请求时,比如:多级请求、请假/加薪
    等审批流程、Java Web中Tomcat对Encoding的处理、拦截器

三.责任链模式如何解耦
在责任链模式中,作为请求接收者的多个对象通过对其后继的引用而连接起来形成一条链。请求在这条链上传递,直到这条链上的某一个接收者处理该请求。每个接收者都可以选择执行处理请求或是向后继传递请求。发出请求的客户端并不知道链上的哪一个接收者会处理请求,从而实现了客户端与接收者之间的解耦。

责任链模式真的是一个好的方案吗?
不符合开闭原则:要依据项目的实际情况而定
性能考量

四.责任链模式的应用

  • Java中的异常处理机制

在这里插入图片描述
在这里插入图片描述
FilterChain并不是一个纯粹的责任链,在上面例子的责任链中,同一时间只有一个handler处理请求,而在FilterChain中同一时间可以有多个handler处理请求。

Spring Security中的Filter Chain。

  • SpringMVC-HandlerExecutionChain
    在这里插入图片描述
  • springmvc 请求的流程图中,执行了 拦截器相关方法 interceptor.preHandler 等等
  • 在处理SpringMvc请求时,使用到职责链模式还使用到适配器模式
  • HandlerExecutionChain 主要负责的是请求拦截器的执行和请求处理,但是他本身不处理请求,只是将请求分配给链上注册处理器执行,这是职责链实现方式,减少职责链本身与处理逻辑之间的耦合,规范了处理流程
  • HandlerExecutionChain 维护了 HandlerInterceptor 的集合, 可以向其中注册相应的拦截器.

五.Spring AOP的链式调用
在Spring中,多个AOP如何叠加?就用到了责任链模式,不过和上面不同的是,Spring AOP使用了链式调用来实现多个AOP对同一目标对象的增强。

版本一:

package com.hong.chain;

/**
 * 责任链模式抽象处理类
 */
public abstract class Handler {

    private Handler sucessor;

    public Handler getSucessor() {
        return sucessor;
    }

    public void setSucessor(Handler sucessor) {
        this.sucessor = sucessor;
    }

    public void execute(){
        handleProcess();
        if(sucessor != null){
            sucessor.execute();
        }
    }

    protected abstract void handleProcess();
}
package com.hong.chain;

/**
 * 客户端
 */
public class Client {

    /**
     * 责任链中具体的职责处理类
     */
    static class HandlerA extends Handler{
        @Override
        protected void handleProcess() {
            System.out.println("handle by a");
        }
    }
    static class HandlerB extends Handler{
        @Override
        protected void handleProcess() {
            System.out.println("handle by b");
        }
    }
    static class HandlerC extends Handler{
        @Override
        protected void handleProcess() {
            System.out.println("handle by c");
        }
    }

    public static void main(String[] args){
        Handler handlerA = new HandlerA();
        Handler handlerB = new HandlerB();
        Handler handlerC = new HandlerC();

        handlerA.setSucessor(handlerB);
        handlerB.setSucessor(handlerC);

        handlerA.execute();
    }
}

改进后的版本二:
在版本一中,需要我们每次手动指定每一个具体Handler的successor handler,能否对这些具体Handler做一个封装,客户端只需要触发一次,内部自动进行链式调用?

package com.hong.chain;

/**
 * 这里的抽象Handler类不再持有successor handler,链式调用交由Chain类去处理
 */
public abstract class ChainHandler {

    public void execute(Chain chain){
        handleProcess();
        chain.proceed();
    }

    protected abstract void handleProcess();
}
package com.hong.chain;

import java.util.List;

/**
 * 自动链式调用核心类,用于盛饭不同的Handler实现类,且顺序调用
 */
public class Chain {

    private List<ChainHandler> handlers;

    private int index = 0;

    public Chain(List<ChainHandler> handlers) {
        this.handlers = handlers;
    }

    public void proceed(){
        if(index >= handlers.size()){
            return ;
        }
        handlers.get(index++).execute(this);
    }
}
package com.hong.chain;

import java.util.Arrays;
import java.util.List;

public class ChainClient {
    static class ChainHandlerA extends ChainHandler{
        @Override
        protected void handleProcess() {
            System.out.println("handle by chain a");
        }
    }
    static class ChainHandlerB extends ChainHandler{
        @Override
        protected void handleProcess() {
            System.out.println("handle by chain b");
        }
    }
    static class ChainHandlerC extends ChainHandler{
        @Override
        protected void handleProcess() {
            System.out.println("handle by chain c");
        }
    }

    public static void main(String[] args){
        List<ChainHandler> handlers = Arrays.asList(
                new ChainHandlerA(),
                new ChainHandlerB(),
                new ChainHandlerC()
        );
        Chain chain = new Chain(handlers);
        chain.proceed();
    }
}

版本一和版本二输出的实现的效果是相同的。

在Spring的org.springframework.aop.framework.ReflectiveMethodInvocation类中,proceed()方法正是采用了类似版本二的链式调用。

@Override
	@Nullable
	public Object proceed() throws Throwable {
		//	We start with an index of -1 and increment early.
		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
			return invokeJoinpoint();
		}

		Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
			// Evaluate dynamic method matcher here: static part will already have
			// been evaluated and found to match.
			InterceptorAndDynamicMethodMatcher dm =
					(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
			Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
			if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
				return dm.interceptor.invoke(this);
			}
			else {
				// Dynamic matching failed.
				// Skip this interceptor and invoke the next in the chain.
				return proceed();
			}
		}
		else {
			// It's an interceptor, so we just invoke it: The pointcut will have
			// been evaluated statically before this object was constructed.
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
		}
	}


	/**
	 * Invoke the joinpoint using reflection.
	 * Subclasses can override this to use custom invocation.
	 * @return the return value of the joinpoint
	 * @throws Throwable if invoking the joinpoint resulted in an exception
	 */
	@Nullable
	protected Object invokeJoinpoint() throws Throwable {
		return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
	}

六.两种实现方式的比较
上面改进后的版本二是Tomcat、Spring、java web api中的实现方式,当然更简单的实现方式是最开始介绍的那种方式,每个filter持有下一个filter的引用,处理完之后直接调用nextFilter的方法,直到执行完毕,显然这种方式在编码上更简单也更容易理解。那为什么许多java框架要采用类似改进后的版本二的方式呢?个人认为主要是扩展性上。Chain持有ChainHandler的列表,自然拥有对所有ChainHandler的管理和控制。这样就把对整个Handler-Chain链的管理集中在一个地方,实现了维护和扩展的方便。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值