Skip to content

策略

这在项目中多处使用。例如:后台活动商品冲突功能,小程序多支付渠道选择。

定义

顾名思义,策略模式(Strategy Pattern)定义了一系列算法或策略,并将每个算法封装在独立的类中,使得它们可以互相替换。通过使用策略模式,可以在运行时根据需要选择不同的算法,而不需要修改客户端代码。

什么时候使用它

一个模块有许多类,而区分它们的只是行为。

优缺点

优点:

  • 算法可以自由切换。
  • 避免使用多重条件判断。
  • 扩展性良好。

缺点:

  • 策略类会增多。
  • 所有策略类都需要对外暴露。

核心实现

  • 环境(Context)

维护一个对策略对象的引用,负责将客户端请求委派给具体的策略对象执行。环境类可以通过依赖注入、简单工厂等方式来获取具体策略对象。

  • 抽象策略(Abstract Strategy)

定义了策略对象的公共接口或抽象类,规定了具体策略类必须实现的方法。

  • 具体策略(Concrete Strategy)

实现了抽象策略定义的接口或抽象类,包含了具体的算法实现。

示例

需求是,小程序端支付将会提供多种支付渠道供选择。

先来定义一个环境(Context):

java
/**
 * 抽象策略工厂
 *
 * @author mjyang
 * @date 2023/5/23 17:03
 */
@Component
public class AbstractStrategyFactory implements CommandLineRunner {

    /**
     * 策略集合
     */
    private final Map<String, AbstractStrategy> abstractStrategyHashMap = MapUtil.newHashMap();

    /**
     * 根据 mark 获取具体策略
     *
     * @param mark 策略标识
     * @return 实际执行策略
     */
    public AbstractStrategy get(String mark) {
        return Optional.ofNullable(abstractStrategyHashMap.get(mark)).orElseThrow(() -> new RuntimeException(String.format("%s 策略未定义", mark)));
    }

    /**
     * 根据 mark 查询具体策略并执行
     *
     * @param mark 策略标识
     * @param t    策略入参
     * @param <T>  策略入参范型
     */
    public <T> void execute(String mark, T t) {
        AbstractStrategy strategy = get(mark);
        strategy.execute(t);
    }

    /**
     * 根据 mark 查询具体策略并执行,带返回结果
     *
     * @param mark 策略标识
     * @param t    策略入参
     * @param <T>  策略入参范型
     * @param <R>  策略出参范型
     * @return <R>
     */
    public <T, R> R executeResp(String mark, T t) {
        AbstractStrategy strategy = get(mark);
        return (R) strategy.executeResp(t);
    }

    @Override
    public void run(String... args) {
        Map<String, AbstractStrategy> strategyMap = SpringUtil.getBeansOfType(AbstractStrategy.class);
        strategyMap.forEach((beanName, bean) -> {
            if (abstractStrategyHashMap.containsKey(bean.mark())) {
                throw new RuntimeException(String.format("%s duplicate execution policy", bean.mark()));
            }
            abstractStrategyHashMap.put(bean.mark(), bean);
        });
    }
}

再来定义抽象策略(Abstract Strategy):

java
/**
 * 策略抽象接口
 *
 * @author mjyang
 * @date 2023/5/23 17:03
 */
public interface AbstractStrategy<T, R> {

    /**
     * 策略标识
     *
     * @return 策略标识
     */
    String mark();

    /**
     * 执行策略
     *
     * @param t 策略入参
     */
    default void execute(T t) {

    }

    /**
     * 执行策略,带返回值
     *
     * @param t 策略入参
     * @return 策略后返回值
     */
    default R executeResp(T t) {
        return null;
    }
}

再来定义具体策略(Concrete Strategy):

java
/**
 * 微信支付类
 *
 */
public class WxPayService implements AbstractStrategy<PayRequestDto, Void> {

    @Override
    public String mark() {
        return "wx_pay";
    }
    
    @Override
    public void execute(PayRequestDto dto) {
        ...
    }
}
java
/**
 * 支付宝支付类
 *
 */
public class AlipayPayService implements AbstractStrategy<PayRequestDto, Void> {

    @Override
    public String mark() {
        return "alipay_pay";
    }
    
    @Override
    public void execute(PayRequestDto dto) {
        ...
    }
}

接下来就是客户端调用了:

java
public static void main(String[] args) {
    SpringUtil.getBean(AbstractStrategyFactory.class).get("wx_pay");
}

以上这个示例是一个通用策略,通过这个简单的示例,演示了如何来使用策略模式。