手写一个责任链,实战一个责任链

807 阅读3分钟

责任链模式经常用于复杂多变的业务场景,比如电商商品价格的计算,源码中也有运用,比如servlet的过滤器,mybatis的动态接口等等,本篇文章从手写一个责任链开始,带你了解,最后开始实战

手写责任链demo

责任链的链管理器,有人直接命名XXXChain,但我一般习惯写成XXXChainManager
public interface FilterChainManager {
    public void doFilter();
}

写这个链管理器的实现类,一般用Autowired将过滤器都注入到filters当中,spring有自动注入的功能,也可以重写InitialBeanLize来实现
写一个与过滤器方法重名的方法doFilter,递归调用,在每隔过滤器中都单独调用了链管理器的 doFilter
public class ApplicationFilterChainManager implements FilterChainManager {

    private Filter[] filters = new Filter[10];
    private Servlet servlet = null;

    // 总共的Filter数目
    private int n;

    // 当前执行完的filter数目
    private int pos;

    @Override
    public void doFilter() {
        if (pos < n) {
            Filter curFilter = filters[pos++];
            curFilter.doFilter(this);
            return;
        }
        servlet.service();
    }

    public void addFilter(Filter filter) {
        // 这里源码有动态扩容的过程,和ArrayList差不多
        // 我就不演示了,直接赋数组大小为10了
        filters[n++] = filter;
    }

    public void setServlet(Servlet servlet) {
        this.servlet = servlet;
    }
}
过滤器的接口,直接将链管理器作为入参,方便递归调用doFilter
public interface Filter {
    public void doFilter(FilterChainManager chain);
}
过滤器的具体实现类
public class ImageFilter implements Filter {
    @Override
    public void doFilter(FilterChainManager chain) {
        System.out.println("ImageFilter执行了");
        chain.doFilter();
    }
}
public class LogFilter implements Filter {
    @Override
    public void doFilter(FilterChainManager chain) {
        System.out.println("LogFilter执行了");
        chain.doFilter();
    }
}
public class Test {

    public static void main(String[] args) {
       
        ApplicationFilterChainManager applicationFilterChain = new ApplicationFilterChainManager();
        applicationFilterChain.addFilter(new LogFilter());
        applicationFilterChain.addFilter(new ImageFilter());
        applicationFilterChain.setServlet(new MyServlet());

        // LogFilter执行了
        // MyServlet的service方法执行了
        applicationFilterChain.doFilter();
    }
}

具体实战

  • 职责链模式处理电商系统计算订单金额
@Component
public class OrderHandlerChainManager implements ApplicationContextAware {
    private List<AbstractHandler> chain = new ArrayList<>(10);

    public void handle(OrderHandleContext context) {
        if (context.getPos() < chain.size()) {
            AbstractHandler handler = chain.get(context.getPos());
            // 移动位于处理器链中的位置
            context.setPos(context.getPos() + 1);
            handler.doHandle(context, this);
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, AbstractHandler> beans = applicationContext.getBeansOfType(AbstractHandler.class);
        chain.addAll(beans.values());
        // 根据处理器的权重,对处理器链中元素进行排序
        chain.sort(Comparator.comparingInt(AbstractHandler::weight));
    }
}
把要处理的参数都封装在上下文中,作为doHandle的入参,具体的实现类中的doHandle方法有两个入参:OrderHandleContext和链管理器
@Data
@Accessors(chain = true)
public class OrderHandleContext {
    /**
     * 当前处理器位于处理器 chain 上的位置
     */
    private int pos;


    /**
     * 订单号
     *
     */
    private String orderNo;


    /**
     * 订单金额
     */
    private Double amount;


    /**
     *  VIP等级
     */
    private Integer vipLevel;


    /**
     * 优惠券
     */
    private String couponNo;
}
链路实体的抽象类
public abstract class AbstractHandler {
    protected abstract void doHandle(OrderHandleContext context, OrderHandlerChainManager chain);

    /**
     * 订单处理器的权重
     */
    protected abstract int weight();
}
具体的链路实体1
@Component
public class SalesOrderHandler extends AbstractHandler {
    @Override
    protected void doHandle(OrderHandleContext context, OrderHandlerChainManager chain) {
        Double amount = context.getAmount();
        if (amount > 80) {
            context.setAmount(amount * 0.9);
        }
        // 调用下一个处理器进行处理
        chain.doHandle(context);
    }

    @Override
    protected int weight() {
        return OrderHandlerWeightEnum.SALES.getCode();
    }
}
具体的链路实体2
@Component
public class VipOrderHandler extends AbstractHandler {
    @Override
    protected void doHandle(OrderHandleContext context, OrderHandlerChainManager chain) {
        if (context.getVipLevel() > 2) {
            context.setAmount(context.getAmount() - 5);
        }
        // 调用下一个处理器进行处理
        chain.doHandle(context);
    }

    @Override
    protected int weight() {
        return OrderHandlerWeightEnum.VIP.getCode();
    }
}
具体的链路实体3
@Component
public class CouponOrderHandler extends AbstractHandler {

    @Override
    protected void doHandle(OrderHandleContext context, OrderHandlerChainManager chain) {
        if (context.getCouponNo() != null) {
            context.setAmount(context.getAmount() - 10);
        }
        // 调用下一个处理器进行处理
        chain.doHandle(context);
    }

    @Override
    protected int weight() {
        return OrderHandlerWeightEnum.COUPON.getCode();
    }
}
责任链测试
@SpringBootTest(classes = APP.class)
public class ChainTests {

  @Resource
  private OrderHandlerChainManager chain;

  /**
   * 应支付金额=订单金额-优惠券优惠金额-促销活动优惠金额-会员权益优惠金额
   *参照 Tomcat 源码中 Filter 的作法,引入了 Chain 类,统一对处理器封装为链,减少客户端使用时出错的可能
   */
  @Test
  public void name() {
    OrderHandleContext order = new OrderHandleContext()
            .setOrderNo("123")
            .setAmount(100d)
            .setVipLevel(3)
            .setCouponNo("111");
    chain.doHandle(order);
    System.out.println("订单最终金额为: " + order.getAmount());
  }

曾经去阿里面试到第三轮的时候,有个面试官就让我手写过责任链,足可见责任链模式的重要性 掌握这几个点,让你手写也不慌

  • 链路管理器中注入链,链就是一个个实例的list集合
  • 链路实体的抽象类
  • 链路实体
  • 链路实体的方法和链路管理器的方法一致,链路管理器的方法的入参:上下文,链路实体的方法入参有上下文和链路管理器实例
  • 上下文当中包括pos,处理到的实例的位置