责任链模式经常用于复杂多变的业务场景,比如电商商品价格的计算,源码中也有运用,比如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,处理到的实例的位置