Bootstrap

责任链模式

07-责任链模式

1 导读

1.1 定义

包含了一些命令对象和一系列处理对象。每个处理对象决定它能处理哪些命令对象,它也知道如何将它不能处理的命令对象传递给该链中的下一个处理对象。

该模式还描述了往该处理链的末尾添加新的处理对象的方法。

精简定义:为请求创建一个接收此次请求对象的链。

1.2 类型

行为型。

2 适用场景

一个请求的处理需要多个对象当中的一或几个协作处理。

3 优点

请求的发送者和接收者(请求的处理)解耦,责任链可动态组合。

4 缺点

  • 责任链太长或者处理时间过长,影响性能
  • 责任链有可能过多

5 设计模式对比

5.1 V.S 状态模式

  • 各对象并不指定下一个所要处理的对象者,只有在客户端类设置链顺序及元素,直到被某个责任链处理或整条链结束
  • 每个状态知道自己下一个所要处理的对象者是谁,即编译时确定

6 实战

package com.javaedge.design.pattern.behavioral.chainofresponsibility;

import lombok.Getter;
import lombok.Setter;

/**
 * @author JavaEdge
 * @date 2019/1/19
 */

@Getter
@Setter
public class Course {

    /**
     * 课程名
     */

    private String name;

    /**
     * 博客
     */

    private String article;

    /**
     * 视频
     */

    private String video;
}
package com.javaedge.design.pattern.behavioral.chainofresponsibility;

import org.apache.commons.lang3.StringUtils;

/**
 * 【具体实现类】审批人
 *
 * @author JavaEdge
 */

public class ArticleApprover extends BaseApprover {

    @Override
    public void deploy(Course course) {
        if (StringUtils.isNoneBlank(course.getArticle())) {
            System.out.println(course.getName() + "含博客,批准!");
            if (nextApprover != null) {
                nextApprover.deploy(course);
            }
        } else {
            System.out.println(course.getName() + "不含博客,拒绝!");
        }
    }
}
package com.javaedge.design.pattern.behavioral.chainofresponsibility;

/**
 * 审批人
 *
 * @author JavaEdge
 */

public abstract class BaseApprover {

    protected BaseApprover nextApprover;

    public void setNextApprover(BaseApprover baseApprover) {
        this.nextApprover = baseApprover;
    }

    /**
     * 发布
     *
     * @param course 课程
     */

    public abstract void deploy(Course course) ;
}
package com.javaedge.design.pattern.behavioral.chainofresponsibility;

import org.apache.commons.lang3.StringUtils;

import java.util.Objects;

/**
 * 【具体实现类】审批人
 *
 * @author JavaEdge
 */

public class VideoApprover extends BaseApprover {

    @Override
    public void deploy(Course course) {
        if (StringUtils.isNoneEmpty(course.getVideo())) {
            System.out.println(course.getName() + "含视频,批准!");
            if (Objects.nonNull(nextApprover)) {
                nextApprover.deploy(course);
            }
        } else {
            System.out.println(course.getName() + "不含视频,拒绝!");
        }
    }
}

UML:

alt

测试类:

package com.javaedge.design.pattern.behavioral.chainofresponsibility;

/**
 * @author JavaEdge
 */

public class Test {

    public static void main(String[] args) {
        BaseApprover articleApprover = new ArticleApprover();
        BaseApprover videoApprover = new VideoApprover();

        Course course = new Course();
        course.setName("公众号-Learner");
        course.setArticle("JavaEdge的博客");
        course.setVideo("JavaEdge的小视频");

        articleApprover.setNextApprover(videoApprover);
        articleApprover.deploy(course);
    }
}

output:
公众号-Learner含博客,批准!
公众号-Learner含视频,批准!

将博客注释掉:

public static void main(String[] args) {
    BaseApprover articleApprover = new ArticleApprover();
    BaseApprover videoApprover = new VideoApprover();

    Course course = new Course();
    course.setName("公众号-Learner");
//        course.setArticle("JavaEdge的博客");
    course.setVideo("JavaEdge的小视频");

    articleApprover.setNextApprover(videoApprover);
    articleApprover.deploy(course);
}

output:
公众号-Learner不含博客,拒绝!

7 框架应用

7.1 Tomcat#FilterChain

FilterChain 是一个由 Servlet 容器提供给开发人员的对象,提供一个对资源的过滤请求的调用链的视图。 过滤器使用 FilterChain 调用链中的下一个过滤器,或如果调用过滤器是链中的最后一个过滤器,则调用链末尾的资源

public interface FilterChain {

    // 导致调用链中的下一个筛选器,或者如果调用筛选器是链中的最后一个筛选器,
    // 则会导致调用链末尾的资源
    public void doFilter(ServletRequest request, ServletResponse response)
            throws IOException, ServletException
;
}

8 项目实战

public final class AbstractChainContext<Timplements CommandLineRunner {
    
    private final Map<String, List<AbstractChainHandler>> abstractChainHandlerContainer = Maps.newHashMap();
    
    /**
     * 责任链组件执行
     *
     * @param mark         责任链组件标识
     * @param requestParam 请求参数
     */

    public void handler(String mark, T requestParam) {
        List<AbstractChainHandler> abstractChainHandlers = abstractChainHandlerContainer.get(mark);
        if (CollectionUtils.isEmpty(abstractChainHandlers)) {
            throw new RuntimeException(String.format("[%s] Chain of Responsibility ID is undefined.", mark));
        }
        abstractChainHandlers.forEach(each -> each.handler(requestParam));
    }
    
    @Override
    public void run(String... args) throws Exception {
        Map<String, AbstractChainHandler> chainFilterMap = ApplicationContextHolder
                .getBeansOfType(AbstractChainHandler.class);
        chainFilterMap.forEach((beanName, bean) -> {
            List<AbstractChainHandler> abstractChainHandlers = abstractChainHandlerContainer.get(bean.mark());
            if (CollectionUtils.isEmpty(abstractChainHandlers)) {
                abstractChainHandlers = new ArrayList();
            }
            abstractChainHandlers.add(bean);
            List<AbstractChainHandler> actualAbstractChainHandlers = abstractChainHandlers.stream()
                    .sorted(Comparator.comparing(Ordered::getOrder))
                    .collect(Collectors.toList());
            abstractChainHandlerContainer.put(bean.mark(), actualAbstractChainHandlers);
        });
    }
}

抽取组件接口:

/**
 * 抽象业务责任链组件
 *
 * @author JavaEdge
 * @github <a href="https://github.com/Java-Edge" />
 * @公众号 JavaEdge,关注回复:架构师,领取后端架构师成长手册
 */

public interface AbstractChainHandler<Textends Ordered {
    
    /**
     * 执行责任链逻辑
     *
     * @param requestParam 责任链执行入参
     */

    void handler(T requestParam);
    
    /**
     * @return 责任链组件标识
     */

    String mark();
}

应用启动时加载所有责任链的组件,根据标识和order排序,保证执行顺序。

创建订单抽象出3个责任组件,分别order-0,-1,-2,标识为ORDER_CREATE_FILTER

/**
 * 订单创建参数必填检验
 *
 * @author JavaEdge
 * @github <a href="https://github.com/Java-Edge" />
 * @公众号 JavaEdge,关注回复:架构师,领取后端架构师成长手册
 */

@Component
public final class OrderCreateParamNotNullChainHandler implements OrderCreateChainFilter<OrderCreateCommand{
    
    @Override
    public void handler(OrderCreateCommand requestParam) {
        if (Objects.isNull(requestParam.getCustomerUserId())) {
            throw new ClientException(OrderCreateErrorCodeEnum.CUSTOMER_USER_ID_NOTNULL);
        } else if (Objects.isNull(requestParam.getTotalAmount())) {
            throw new ClientException(OrderCreateErrorCodeEnum.TOTAL_AMOUNT_NOTNULL);
        } else if (Objects.isNull(requestParam.getPayAmount())) {
            throw new ClientException(OrderCreateErrorCodeEnum.PAY_AMOUNT_NOTNULL);
        } else if (Objects.isNull(requestParam.getFreightAmount())) {
            throw new ClientException(OrderCreateErrorCodeEnum.FREIGHT_AMOUNT_NOTNULL);
        } else if (Objects.isNull(requestParam.getSource())) {
            throw new ClientException(OrderCreateErrorCodeEnum.SOURCE_NOTNULL);
        }
    }
    
    @Override
    public int getOrder() {
        return 0;
    }
}
/**
 * 订单创建责任链过滤器
 *
 * @author JavaEdge
 * @github <a href="https://github.com/Java-Edge" />
 * @公众号 JavaEdge,关注回复:架构师,领取后端架构师成长手册
 */

public interface OrderCreateChainFilter<T extends OrderCreateCommandextends AbstractChainHandler<OrderCreateCommand{
    
    @Override
    default String mark() {
        return OrderChainMarkEnum.ORDER_CREATE_FILTER.name();
    }
}

最后执行:

alt

泛型类

2.1 AbstractChainContext<T>

实现 CommandLineRunner 接口。它表示一个责任链上下文,用于执行责任链组件。

  • abstractChainHandlerContainer 是一个用于存储责任链组件的容器,使用 Map 数据结构来存储不同标识的责任链组件列表。每个标识对应一个责任链组件列表,列表中的责任链组件按照其顺序进行处理
  • handler 方法用于执行责任链组件。它接收一个标识 mark 和一个请求参数 requestParam,根据标识从容器中获取对应的责任链组件列表,并依次调用每个组件的 handler 方法进行处理
  • run 方法,通过 ApplicationContextHolder.getBeansOfType(AbstractChainHandler.class) 获取所有实现了 AbstractChainHandler 抽象类的 bean,然后遍历每个 bean,并将其添加到对应标识的责任链组件列表中。之后,对每个责任链组件列表进行排序,并更新到容器中

通过实现 CommandLineRunner 接口,该类可在 Spring Boot 应用程序启动时自动执行,并根据配置的责任链组件进行初始化和排序。之后,可以通过调用 handler 方法来执行责任链组件的处理逻辑。

2.2 抛出异常->执行中断

责任链模式中,当一个处理程序(或处理者)处理请求时,它可以选择继续将请求传递给下一个处理程序,也可以选择终止责任链的执行。

如果某个处理程序在处理请求时发生了异常,并且没有进行适当的处理或捕获该异常,那么该异常将向上级调用栈抛出,可能导致责任链的执行被中断。

在这种情况下,除非有适当的异常处理机制或错误处理策略来捕获和处理异常,否则可能会导致责任链的后续处理程序不会执行。

要确保在责任链中的处理程序发生异常时能够正确处理,建议在每个处理程序中使用适当的异常处理机制(例如 try-catch 块)来捕获和处理异常。这样可以防止异常在责任链中传播,并保证后续的处理程序能够继续执行。

获取更多干货内容,记得关注我哦。

本文由 mdnice 多平台发布

;