SpringBoot策略模式通用方案
策略模式简介
策略模式(Strategy Pattern)是软件设计模式中的一种行为模式。
作为软件设计模式的一种,其目的是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。
从软件工程的角度来讲,运用策略模式这样的设计模式最终目的是为了设计出高内聚、低耦合的高质量软件。
策略模式理解
策略模式:策略二字,顾名思义,是为了实现某种目标而制定的方法,就像诸葛亮隆中对为刘备制定的三分天下的策略。
而在软件开发中,以面向对象为例,有时候对象需要获取某种结果,在此期间需要不同的方法策略
现实中举例子:比如说去超市买东西想要花钱,对象可以选择微信支付、支付宝支付、云闪付支付等等方式
我们把这一系列的方法抽象出来的一种方式,规范化演变成了策略模式
在porcessOn中看到的觉得不错,如下图所示:
-
Strategy是接口,定义策略的抽象方法
-
Strategy1和Strategy2是接口实现,实现不同的策略
-
Context作为策略上下文,用于管理和使用策略,作为策略的管理者,有很好的拓展性,比如说策略需要一些公共的前置校验、后置操作等。
上面这个是标准的Java中的实现,以下内容又结合了工厂模式,将Context改为一个工厂,用于生产和交付策略方法,使用更为灵活。
策略模式结合SpringBoot
-
在SpringBoot项目中,主要使用到了spring的IoC(Inversion of Control),我们将创建对象交给了spring容器管理;
策略模式中我们就可以将不同策略的对象交给spring容器来管理,不同方法实现加上注解如:@Component
-
我们需要更方便的注册使用,仅仅交给spring容器是不够的,所以可以结合工厂模式,把不同的策略注册到工厂的map中去统一管理
通过实现spring的容器初始化的接口
implements InitializingBean
,实现afterPropertiesSet
方法,在bean初始化完成时将其注册到工厂 -
在使用时候,我们通过工厂根据类型来获取不同的策略方法,来执行策略,至此每次修改和开发策略,只更改不同的strategy,而无需修改业务上下文,做到了一定程度的解耦,而不同的策略在不同的实现类,互不干扰,内聚程度也得到了提升,对于软件工程的质量也有一定的提升。
需要注意:
- 通用的策略模式一般需要确保所有的策略的输入和输出是确定的,以去超市买东西举例,买东西需要的输入是支付的金额等信息,输出是支付结果成功失败等信息是确定的,否则的话需要结合泛型来使用了。
- 通过不同的类型区分,来执行不同的策略,以去超市买东西举例,类型可分为:a微信b支付宝和c云闪付,a对应微信支付的策略方法,b对应支付保的策略方法。
代码实现
策略接口
定义好一组策略方法共同的入参出参的规范即可
/**
* 策略接口
* @author HB、ocean
* @date 2024/08/22
*/
public interface Strategy {
/**
* 策略注册方法
* 注:策略实现类需实现InitializingBean方法,编写afterPropertiesSet抽象方法的实现,调用此方法,进行策略注册
*
* @param code
*/
default void afterPropertiesSet(String code) {
StrategyFactory.register(code, this);
}
/**
* 业务逻辑A
*
* @param reqDTO 业务入参DTO
* @return {@link RespDTO}
*/
RespDTO businessA(ReqDTO reqDTO);
/**
* 业务逻辑B
*
* @param reqDTO 业务入参DTO
* @return {@link RespDTO}
*/
RespDTO businessB(ReqDTO reqDTO);
}
策略实现
实现策略接口,实现抽象方法,并且将不同的策略注册到策略上下文工厂
示例只写了一个默认策略实现,不同的策略类似就不写了哈
/**
* 默认策略实现
* @author HB、ocean
* @date 2024/08/23
*/
@Component
@Slf4j
public class DefaultStrategyImpl implements Strategy, InitializingBean {
@Override
RespDTO businessA(ReqDTO reqDTO) {
// 业务a策略实现 todo
}
@Override
RespDTO businessB(ReqDTO reqDTO) {
// 业务b策略实现 todo
}
@Override
public void afterPropertiesSet() throws Exception {
afterPropertiesSet(Constant.DEFAULT_STRATEGY_TYPE);
}
}
策略工厂
用于管理策略,执行策略
/**
* 策略工厂类
* (根据类型来区分,注册和获取不同的策略方法)
* @author HB、ocean
* @date 2024/08/22
*/
@Slf4j
public final class StrategyFactory {
/**
* 私有化构造器
*/
private StrategyFactory() {
}
/**
* 集合存储,key 策略类型,value 策略对象
*/
private static final Map<String, Strategy> STRATEGY_CONCURRENT_HASH_MAP = new ConcurrentHashMap<>();
/**
* 提供获取策略的方法
*
* @param bankCode
* @return {@link Strategy}
*/
public static Strategy getStrategy(String type) {
// todo 可以进行前置校验
Strategy sendService = STRATEGY_CONCURRENT_HASH_MAP.get(type);
return sendService;
}
/**
* 在Bean属性初始化后执行该方法
*
* @param type 类型
* @param strategy
*/
public static void register(String type, Strategy strategy) {
LOGGER.info("注册type: {},对应的处理策略...", type);
STRATEGY_CONCURRENT_HASH_MAP.put(type, strategy);
}
}
总结
策略模式看似繁琐的一系列规范,涉及到接口、实现、上下文,但实际上对于代码质量的提升功不可没……
-
想象下,如果不用策略模式,我们在业务代码中使用一些if-else,前期开发进度快了,但随着需求增加,代码行数增多,业务逻辑耦合会非常高,不太利于后期的维护和更改。
-
反观在策略模式的规范下,哪个策略改了,我只改那个策略的实现类;如果修改了公共的内容,修改接口或者策略上下文工厂即可;新增和删除策略,都无需修改业务代码。
当然这也只是作者的一方观点(ps:阿里规约中也有提到当超过 3 层的 if-else 的逻辑判断代码可以使用策略模式来实现。)
欢迎各位大佬提出意见😄