🤕告别 if 第三讲(业务策略)共四讲

71 阅读6分钟

一、使用背景

在公司进行业务开发中,随着业务的复杂性不断提升,开发者常常需要为不同的业务场景设计不同的策略。传统的做法往往依赖于条件判断或大量的 if-else 语句来实现策略的切换。为了更好地解决这些问题,策略模式应运而生。策略模式允许定义一系列算法,将每个算法封装起来,并使它们可以互换。

在 Java 中,策略模式的实现有许多种方式,其中使用函数式编程特性来实现更加灵活的策略模式,是一种非常高效且现代的做法。本文将对一个基于 Java 函数式编程的策略工具类 BizStrategy 进行分析,探讨其设计原理和实际应用。

二、策略模式对比

2.1 使用传统策略模式

在没有 BizStrategy 工具类之前,策略模式的实现可能会像这样:

// 传统的策略接口
interface Strategy {
    String execute(String context);
}

// 策略A
class StrategyA implements Strategy {
    @Override
    public String execute(String context) {
        return "策略A执行结果:" + context;
    }
}

// 策略B
class StrategyB implements Strategy {
    @Override
    public String execute(String context) {
        return "策略B执行结果:" + context;
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        String strategyType = "A";  // 假设选择了策略A
        String context = "上下文";
        
        Strategy strategy;
        if ("A".equals(strategyType)) {
            strategy = new StrategyA();
        } else if ("B".equals(strategyType)) {
            strategy = new StrategyB();
        } else {
            throw new IllegalArgumentException("没有找到对应的策略:" + strategyType);
        }
        
        System.out.println(strategy.execute(context));
    }
}

在这个传统的实现中,我们需要通过大量的条件判断来选择不同的策略。每次添加一个新的策略,都需要修改客户端代码,维护起来非常繁琐,且代码重复度高。

2.2 使用业务策略工具

使用 BizStrategy 工具类后,代码变得更加简洁,策略的注册和执行也变得更加灵活。

public class Client {
    public static void main(String[] args) {
        // 创建策略工具类实例
        BizStrategy<String, String, String> strategy = new BizStrategy<>();
        
        // 注册策略
        strategy.registerFunction("A", context -> "策略A执行结果:" + context);
        strategy.registerFunction("B", context -> "策略B执行结果:" + context);

        // 执行策略
        String context = "上下文";
        System.out.println(strategy.execute("A", context));  // 输出: 策略A执行结果:上下文
        System.out.println(strategy.executeOrDefault("BB", context, ctx -> "默认策略执行结果:" + ctx));  // 输出: 默认策略执行结果:上下文
    }
}

在这个实现中,我们通过 BizStrategy 类注册了策略A和策略B,并直接通过 execute 方法执行策略。即使没有找到对应的策略,executeOrDefault 方法也可以提供默认策略,避免了大量的条件判断。新增策略时,只需要注册新的策略,不需要修改现有代码。

三、介绍业务策略

3.1 使用步骤

1.注册策略:BizStrategy 可以非常灵活地注册不同类型的策略。此外,registerIf 方法提供了条件注册策略的能力,只有在条件满足时才会注册对应的策略。

strategy.registerFunction("A", context -> "策略A执行结果:" + context);
strategy.registerFunction("B", context -> "策略B执行结果:" + context);

2.执行策略: 执行策略时,BizStrategy 提供了多个重载的 execute 方法,可以根据策略的不同参数类型来选择适当的执行方式。

String context = "上下文";
System.out.println(strategy.execute("A", context));  // 输出: 策略A执行结果:上下文

3.2 支持的注册策略类型

BizStrategy 通过不同的函数接口来支持多种策略类型,具体包括:

  • Function:接受一个参数并返回结果的策略。
  • Runnable:无参数且无返回值的策略。
  • Consumer:接受一个参数但没有返回值的策略。
  • Supplier:无参数且有返回值的策略。
  • Predicate:接受一个参数并返回布尔值的策略。

四、文章总结

BizStrategy 工具类提供了一个现代化的、基于 Java 函数式编程的策略管理方案。通过对策略的灵活注册与执行,它使得策略模式在实际应用中更加简洁、高效,并且具备很好的扩展性。相比传统的策略模式实现,BizStrategy 更加灵活,能够动态注册策略、条件注册策略,并支持多种类型的策略。

五、附上代码

/**
 * @author: bdmcom
 * @createTime: 2024/12/14 20:39
 * @company: <a href="https://www.bdmcom.cn">本当迷博客</a>
 * @description: 策略工具类
 */
@Slf4j
public class BizStrategy<T, P, R> {

    public static final String STRATEGY_NOT_FOUND = "没有找到对应的策略:";

    private final ConcurrentMap<T, Function<P, R>> strategies = new ConcurrentHashMap<>();

    /**
     * 注册一个策略(Function 类型)
     */
    public void registerFunction(T key, Function<P, R> action) {
        strategies.put(key, action);
    }

    /**
     * 注册一个策略(Runnable 类型,接受无参数但没有返回值)
     */
    public void registerRunnable(T key, Runnable action) {
        strategies.put(key, context -> {
            action.run();
            return null;  // 返回null,表示没有返回值
        });
    }

    /**
     * 注册一个策略(Consumer 类型,接受一个参数但没有返回值)
     */
    public void registerConsumer(T key, Consumer<P> action) {
        strategies.put(key, t -> {
            action.accept(t);
            return null;
        });
    }

    /**
     * 注册一个策略(Supplier 类型,接受无参数并返回结果)
     */
    public void registerSupplier(T key, Supplier<R> action) {
        strategies.put(key, t -> action.get());
    }

    /**
     * 注册一个策略(Predicate 类型,接受一个参数并返回布尔值)
     */
    @SuppressWarnings("all")
    public void registerPredicate(T key, Predicate<P> action) {
        strategies.put(key, t -> action.test(t) ? (R) Boolean.TRUE : (R) Boolean.FALSE);
    }

    /**
     * 执行策略(无参数策略)
     */
    public void execute(T key) {
        Optional.ofNullable(strategies.get(key))
                .orElseThrow(() -> new IllegalArgumentException(STRATEGY_NOT_FOUND + key))
                .apply(null);
    }

    /**
     * 执行无参数的 Supplier 类型策略
     */
    public R executeSupplier(T key) {
        return Optional.ofNullable(strategies.get(key))
                .orElseThrow(() -> new IllegalArgumentException(STRATEGY_NOT_FOUND + key))
                .apply(null);
    }

    /**
     * 执行策略(有参数的策略)
     *
     * @param key     策略键
     * @param context 上下文
     * @return 策略返回的结果
     */
    public R execute(T key, P context) {
        return Optional.ofNullable(strategies.get(key))
                .orElseThrow(() -> new IllegalArgumentException(STRATEGY_NOT_FOUND + key))
                .apply(context);
    }

    /**
     * 执行策略,如果没有找到对应的策略则执行默认操作
     *
     * @param key           策略键
     * @param context       上下文
     * @param defaultAction 默认操作
     * @return 策略返回的结果
     */
    public R executeOrDefault(T key, P context, Function<P, R> defaultAction) {
        return strategies.getOrDefault(key, defaultAction).apply(context);
    }

    /**
     * 根据条件动态注册策略
     *
     * @param condition 注册条件
     * @param key       策略键
     * @param action    策略逻辑
     */
    public void registerIf(Predicate<T> condition, T key, Function<P, R> action) {
        if (condition.test(key)) {
            registerFunction(key, action);
        }
    }

    /**
     * 清除所有注册的策略
     */
    public void clearStrategies() {
        strategies.clear();
    }

    /**
     * 移除特定的策略
     *
     * @param key 策略键
     */
    public void removeStrategy(T key) {
        strategies.remove(key);
    }

    @SuppressWarnings("all")
    public static void main(String[] args) {
        log.info("-----------------------策略工具类测试(1入参,1回值)-------------------------------------------------------------");
        // 开始注册策略
        BizStrategy<String, String, String> strategy = new BizStrategy<>();
        strategy.registerFunction("A", context -> "策略A执行结果:" + context);
        strategy.registerFunction("B", context -> "策略B执行结果:" + context);
        strategy.registerFunction("C", context -> "策略C执行结果:" + context);
        strategy.registerIf("D"::equals, "D", context -> "策略D执行结果" + context);
        strategy.registerIf("E"::equals, "E", context -> "策略E执行结果" + context);

        // 执行策略
        log.info(strategy.execute("A", "上下文"));
        log.info(strategy.executeOrDefault("BB", "上下文", context -> "默认策略执行结果:" + context));

        /*// 移除策略
        strategy.removeStrategy("A");
        log.info(strategy.execute("A", "上下文"));

        // 清除所有策略
        strategy.clearStrategies();
        log.info(strategy.execute("B", "上下文"));*/
        log.info("-------------------------------------------------------------------------------------------------");

        log.info("-----------------------策略工具类测试(0入参,0回值)-------------------------------------------------------------");
        // 开始注册策略
        BizStrategy<String, Void, Void> strategy2 = new BizStrategy<>();
        strategy2.registerRunnable("A", () -> log.info("策略A执行结果"));
        strategy2.registerRunnable("B", () -> log.info("策略B执行结果"));
        // 执行策略
        strategy2.execute("A");
        strategy2.execute("B");
        log.info("-------------------------------------------------------------------------------------------------");

        log.info("-----------------------策略工具类测试(1入参,0回值)-------------------------------------------------------------");
        // 开始注册策略
        BizStrategy<String, String, Void> strategy3 = new BizStrategy<>();
        strategy3.registerConsumer("A", context -> log.info("策略A执行结果:" + context));
        strategy3.registerConsumer("B", context -> log.info("策略B执行结果:" + context));
        // 执行策略
        strategy3.execute("A", "上下文");
        strategy3.execute("B", "上下文");
        log.info("-------------------------------------------------------------------------------------------------");

        log.info("-----------------------策略工具类测试(0入参,1回值)-------------------------------------------------------------");
        // 开始注册策略
        BizStrategy<String, Void, String> strategy4 = new BizStrategy<>();
        strategy4.registerSupplier("A", () -> "策略A执行结果");
        strategy4.registerSupplier("B", () -> "策略B执行结果");
        // 执行策略
        log.info(strategy4.executeSupplier("A"));
    }
}