🤕告别 if 第四讲(业务责任链)共四讲

58 阅读10分钟

一、使用背景

在复杂的业务系统中,往往需要执行一系列操作,这些操作可能是条件驱动的,并且每个操作的执行顺序和条件可能都不同。为了更好地管理这些操作,我们可以利用 责任链模式 来实现一系列操作的顺序执行与条件判断。

1.1 什么是责任链模式?

责任链模式是一种行为设计模式,它允许将请求沿着一条链进行传递,直到有对象处理它为止。在这个模式中,每个对象保存对下一个对象的引用。请求会沿着链传递,直到找到一个能够处理该请求的对象。责任链模式的优势在于它实现了请求处理的解耦,便于灵活扩展和修改。

在 Java 中,责任链模式的经典实现方法是将多个处理对象组织成一个链条,每个处理对象检查某个条件,决定是否处理请求,或者将请求传递给下一个处理对象。

1.2 为什么需要 BizChain 工具类?

虽然责任链模式为业务逻辑的分离和解耦提供了强大的支持,但如何在复杂的业务中实现这一模式,却并非易事。很多情况下,我们会面临以下问题:

  • 逻辑耦合性:责任链的多个处理步骤可能需要依赖于一些复杂的业务规则或者优先级,导致实现过程繁琐且难以维护。
  • 链条管理:随着业务流程的变化,责任链可能会变得非常复杂,多个链条可能相互依赖,难以动态添加和删除链条元素。
  • 执行控制:责任链的每个环节执行成功与否,如何灵活地中断或继续执行,控制逻辑可能变得非常复杂。

BizChain 工具类正是为了解决上述问题而设计的。通过使用 BizChain,我们能够以更加灵活和可扩展的方式实现责任链模式,简化链条管理,清晰地控制每个链环的执行顺序和优先级。

二、对比传统责任链

在没有责任链模式的实现时,我们可能需要依赖多个 if-elseswitch-case 语句来逐一判断条件并执行操作。例如,处理一个字符串时,我们可能需要先检查字符串是否包含某些字符,再执行不同的操作。如果条件复杂或操作增多,代码会变得难以维护,且修改某个条件逻辑时可能影响其他部分。

在引入责任链模式后,所有的条件判断和操作被封装为链条中的单独 "链环"。每个链环负责一个条件判断和相应的操作,并且它们之间的执行顺序可以通过优先级灵活控制。添加新的链环不需要修改现有的代码,代码变得更加清晰、可维护。

三、介绍业务责任链

3.1 BizChain 工具类的主要特点

BizChain 工具类是一个泛型工具类,支持动态添加链条元素、优先级排序、条件判断、不同类型的操作执行,并且提供了严格链式执行的机制。下面是一些它的主要特点:

3.1.1 灵活的链条添加

通过 addLink 方法,我们可以根据不同的条件和操作类型,动态地添加新的链环。支持以下两种形式:

  • Predicate 条件和 Consumer 操作:适用于无返回值的操作。
  • Predicate 条件和 Function 操作:适用于有返回值的操作。

这种灵活的设计使得我们可以根据业务需求,快速地添加和修改链条元素,而无需重构整个流程。

3.1.2 优先级排序与控制

每个链环可以设定优先级,优先级值越小,表示该链环的执行优先级越高。BizChain 会自动按照优先级对链环进行排序,并按照顺序执行。通过这种机制,我们能够控制链条中每个环节的执行顺序,确保关键操作优先执行。

3.1.3 严格链式执行与中断机制

BizChain 中,我们提供了一个非常有用的控制机制——严格链式执行。当启用严格链式执行时,如果某个链环执行失败,后续的链环将不再继续执行。该特性对于确保业务流程的完整性和避免无意义的操作非常有用。

此外,通过 interruptOnFailure 参数,我们可以设置是否在某个链环失败时立即中断链条执行。如果开启了中断机制,BizChain 会在链环执行失败时自动中止后续的执行。

3.1.4 异常处理与执行结果记录

BizChain 会记录每个链环的执行结果,包括是否成功执行以及可能出现的错误信息。通过 ChainExecutionResult 类,我们可以查看每个链环的执行状态,以及是否存在异常。这样可以帮助我们清晰地了解整个链条的执行过程,便于调试和优化。

3.1.5 动态链条管理

BizChain 允许我们在链条执行过程中动态地添加新的链环。通过 addLinkDynamically 方法,我们可以在业务流程运行时灵活调整链条结构,满足动态变化的业务需求。

3.2 如何使用 BizChain 工具类?

接下来,我们通过一个简单的示例来演示如何使用 BizChain 工具类来实现业务逻辑的处理。

假设我们需要根据不同的条件处理一个字符串,处理规则如下:

  • 如果字符串以字母 "a" 开头,执行相关操作。
  • 如果字符串包含字母 "b",执行另一个操作。
  • 如果字符串长度大于 5,执行第三个操作。
public static void main(String[] args) {
    BizChain<String> chain = new BizChain<>(true);  // 严格链式执行

    // 添加链环,定义匹配条件和执行操作
    chain.addLink(s -> s.startsWith("a"), s -> log.info("匹配到以a开头的字符串:{}", s), 1);
    chain.addLink(s -> s.contains("b"), s -> log.info("匹配到包含b的字符串:{}", s), 2);
    chain.addLink(s -> s.length() > 5, s -> log.info("匹配到字符串长度大于5:{}", s), 3);

    // 执行链
    try {
        List<ChainExecutionResult> results = chain.execute("abc", true);
        results.forEach(result -> {
            if (result.isExecutedSuccessfully()) {
                log.info("成功执行:{}", result.getLink());
            } else {
                log.error("执行失败:{},错误信息:{}", result.getLink(), result.getErrorMessage());
            }
        });
    } catch (BizChainException e) {
        log.error("链条执行中止:{}", e.getMessage());
    }
}

输出结果

13:15:45.116 [main] INFO cn.bdmcom.common.utils.BizChain - 匹配到以a开头的字符串:abc
13:15:45.118 [main] INFO cn.bdmcom.common.utils.BizChain - 匹配到包含b的字符串:abc
13:15:45.118 [main] INFO cn.bdmcom.common.utils.BizChain - 匹配到字符串长度大于5:abc
13:15:45.123 [main] INFO cn.bdmcom.common.utils.BizChain - 成功执行:BizChain.ChainLink(condition=cn.bdmcom.common.utils.BizChain$$Lambda$2/1712536284@17ed40e0, action=cn.bdmcom.common.utils.BizChain$$Lambda$3/1123225098@50675690, priority=1)
13:15:45.123 [main] INFO cn.bdmcom.common.utils.BizChain - 成功执行:BizChain.ChainLink(condition=cn.bdmcom.common.utils.BizChain$$Lambda$4/606548741@31b7dea0, action=cn.bdmcom.common.utils.BizChain$$Lambda$5/1528637575@3ac42916, priority=2)
13:15:45.123 [main] INFO cn.bdmcom.common.utils.BizChain - 成功执行:BizChain.ChainLink(condition=cn.bdmcom.common.utils.BizChain$$Lambda$6/1190524793@47d384ee, action=cn.bdmcom.common.utils.BizChain$$Lambda$7/472654579@2d6a9952, priority=3)
13:15:45.123 [main] ERROR cn.bdmcom.common.utils.BizChain - 链环条件不匹配,终止执行后续链环: BizChain.ChainLink(condition=cn.bdmcom.common.utils.BizChain$$Lambda$2/1712536284@17ed40e0, action=cn.bdmcom.common.utils.BizChain$$Lambda$3/1123225098@50675690, priority=1)
Exception in thread "main" cn.bdmcom.common.utils.BizChain$BizChainException: 链环条件不匹配,终止执行后续链环

在这个例子中,我们创建了一个 BizChain 实例,配置了三个链环,每个链环有不同的条件判断和优先级。我们使用 strictChain = true,以确保在某个链环执行失败时,后续链环不再执行。

3.3 为什么推荐使用 BizChain 工具类?

3.3.1 简化复杂逻辑

BizChain 把复杂的业务流程拆解成一个个小的链环,每个链环处理一个单独的逻辑。通过这种方式,我们可以将不同的业务处理逻辑进行解耦,易于维护和扩展。

3.3.2 提高可读性和可维护性

通过定义清晰的链条结构和优先级排序,BizChain 提高了代码的可读性和可维护性。每个链环的条件和操作都是独立的,便于调试和修改。

3.3.3 灵活的动态扩展

BizChain 允许在运行时动态添加链环,并支持不同类型的操作。这使得它非常适合需要动态调整的场景,例如根据用户输入或配置文件来决定执行哪些操作。

3.3.4 完善的错误处理机制

BizChain 提供了详细的错误记录和异常处理机制,能够清晰地显示每个链环的执行状态,并在失败时中止后续链环的执行。这对业务流程的控制非常重要。

四、文章总结

BizChain 工具类通过实现责任链模式,提供了一个高效且灵活的方式来组织和管理复杂的条件判断和操作执行。与传统的 if-elseswitch-case 语句相比,责任链模式使得业务处理变得更加清晰、可扩展和易于维护。

  • 高效的优先级排序:链环按优先级排序执行,确保了重要的操作能够先行。
  • 灵活的操作执行:支持不同类型的操作(ConsumerFunction),可以灵活地处理业务需求。
  • 动态链条修改:通过动态添加链环,系统可以随时根据实际需要调整业务流程。
  • 健壮的异常处理:链条执行过程中出现问题时,会通过自定义异常机制进行捕获与处理。

通过 BizChain 类,开发者可以在处理复杂的业务逻辑时,避免传统方案中的代码臃肿和不易维护的问题,从而使得系统的扩展性和可维护性得到了显著提升。

五、附上代码

/**
 * @author: bdmcom
 * @createTime: 2024/12/14 20:39
 * @company: <a href="https://www.bdmcom.cn">本当迷博客</a>
 * @description: 责任链工具类
 */
@Slf4j
public class BizChain<T> {

    private final List<ChainLink<T>> chain = new ArrayList<>();
    /**
     * 是否严格链式执行
     */
    private final boolean strictChain;

    public BizChain(boolean strictChain) {
        this.strictChain = strictChain;
    }


    /**
     * 添加一个链接点,支持设置优先级
     *
     * @param condition 规则条件
     * @param action    执行操作
     * @param priority  链接点的优先级,数值越小优先级越高
     */
    public void addLink(Predicate<T> condition, Consumer<T> action, int priority) {
        chain.add(new ChainLink<>(condition, action, priority));
    }

    /**
     * 添加一个链接点,支持返回结果处理
     *
     * @param condition 规则条件
     * @param action    执行操作,返回结果类型
     * @param <R>       返回结果类型
     */
    public <R> void addLink(Predicate<T> condition, Function<T, R> action) {
        chain.add(new ChainLink<>(condition, action));
    }

    /**
     * 执行链
     *
     * @param context            上下文对象
     * @param interruptOnFailure 是否在某个链接点失败时中断链条
     * @return 链执行结果列表
     */
    @SuppressWarnings("all")
    public List<ChainExecutionResult> execute(T context, boolean interruptOnFailure) {
        List<ChainExecutionResult> results = new ArrayList<>();
        // 按优先级排序链接点
        List<ChainLink<T>> sortedChain = new ArrayList<>(chain);
        sortedChain.sort(Comparator.comparingInt(ChainLink::getPriority));

        // 遍历每个链接点,执行操作
        for (ChainLink<T> link : sortedChain) {
            if (link.condition.test(context)) {
                try {
                    // 执行操作
                    ChainExecutionResult result = new ChainExecutionResult(link, true);
                    result.setExecutedSuccessfully(true);
                    if (link.getAction() instanceof Consumer) {
                        ((Consumer<T>) link.getAction()).accept(context);
                    } else if (link.getAction() instanceof Function) {
                        ((Function<T, ?>) link.getAction()).apply(context);
                    }
                    results.add(result);
                } catch (Exception e) {
                    log.error("执行操作失败", e);
                    results.add(new ChainExecutionResult(link, false, e.getMessage()));

                    // 严格链式执行时,链条失败后终止
                    if (interruptOnFailure && strictChain) {
                        throw new BizChainException("链条执行失败,终止后续链环");
                    }
                }
            } else {
                // 条件不匹配时,严格链式执行中断后续链环
                if (strictChain) {
                    log.error("链环条件不匹配,终止执行后续链环: {}", link);
                    throw new BizChainException("链环条件不匹配,终止执行后续链环");
                } else {
                    results.add(new ChainExecutionResult(link, false, "未匹配条件"));
                }
            }
        }

        if (results.stream().noneMatch(ChainExecutionResult::isExecutedSuccessfully)) {
            log.error("未找到匹配条件,无法执行任何操作");
            throw new BizChainException("未找到匹配条件");
        }

        return results;
    }

    /**
     * 链接点内部类
     *
     * @param <T> 上下文类型
     */
    @Data
    private static class ChainLink<T> {
        private final Predicate<T> condition;
        // 支持不同类型的操作(Consumer、Function)
        private final Object action;
        private final int priority;

        public ChainLink(Predicate<T> condition, Consumer<T> action, int priority) {
            this.condition = condition;
            this.action = action;
            this.priority = priority;
        }

        public ChainLink(Predicate<T> condition, Function<T, ?> action) {
            this.condition = condition;
            this.action = action;
            this.priority = Integer.MAX_VALUE;  // 默认优先级最低
        }
    }

    /**
     * 链接点执行结果
     */
    @Data
    private static class ChainExecutionResult {
        private final ChainLink<?> link;
        private boolean executedSuccessfully;
        private String errorMessage;

        public ChainExecutionResult(ChainLink<?> link, boolean executedSuccessfully) {
            this(link, executedSuccessfully, null);
        }

        public ChainExecutionResult(ChainLink<?> link, boolean executedSuccessfully, String errorMessage) {
            this.link = link;
            this.executedSuccessfully = executedSuccessfully;
            this.errorMessage = errorMessage;
        }
    }

    /**
     * 动态添加链接点
     *
     * @param condition 规则条件
     * @param action    执行操作
     * @param priority  链接点的优先级
     */
    public void addLinkDynamically(Predicate<T> condition, Consumer<T> action, int priority) {
        addLink(condition, action, priority);
    }

    /**
     * 自定义异常
     */
    public static class BizChainException extends RuntimeException {
        public BizChainException(String message) {
            super(message);
        }
    }

    @SuppressWarnings("all")
    public static void main(String[] args) {
        BizChain<String> chain = new BizChain<>(false);
        chain.addLink(s -> s.startsWith("a"), s -> log.info("匹配到以a开头的字符串:{}", s), 1);
        chain.addLink(s -> s.contains("b"), s -> log.info("匹配到包含b的字符串:{}", s), 2);
        chain.addLink(s -> s.length() > 2, s -> log.info("匹配到字符串长度大于5:{}", s), 3);

        // 执行链
        List<ChainExecutionResult> results = chain.execute("abc", true);
        results.forEach(result -> {
            if (result.isExecutedSuccessfully()) {
                log.info("成功执行:{}", result.getLink());
            } else {
                log.error("执行失败:{},错误信息:{}", result.getLink(), result.getErrorMessage());
            }
        });

        // 动态修改链条
        chain.addLinkDynamically(s -> s.contains("xyz"), s -> log.info("匹配到包含xyz的字符串:{}", s), 4);
        results = chain.execute("xyz", true);
        results.forEach(result -> {
            if (result.isExecutedSuccessfully()) {
                log.info("成功执行:{}", result.getLink());
            } else {
                log.error("执行失败:{},错误信息:{}", result.getLink(), result.getErrorMessage());
            }
        });
    }
}