记录一次策略模式和组合模式的实践运用

记录一次策略模式和组合模式的实践运用

背景

最近公司有一个支付路由的功能,大概的需求就是创建一个支付规则,支付规则里可以根据不同的情况,对我们支付通道进行一个切换,比如说我们看设置交易笔数,根据不同的交易笔数去切换不同的支付通道,整体看不是很难,目前主要支持三种情况的设置,但是为了后续的扩展性,所以选择了策略模式+组合模式的一个方式来设计

模式介绍

相信大家都是一个成熟的程序员了,对设计模式其实都比较熟悉,尤其是当我们拥有了spring这一个bean管理的框架,其实很多设计模式都可以很方便实现。

策略模式

就是讲每个行为都封装起来,根据不同的行为去动态调用,例如我们有三个规则交易笔数,交易时间,收银员,那我们就可以定义三个行为去分别对应三个规则,然后根据不同的规则去获取对应的规则类,执行它的行为

组合模式

组合模式就将对象组合成一个集合或者树,调用可以通过一致的方法去遍历调用。例如交易时间规则下,可能还包括一些子规则比如按周循环,按月循环,交易笔数等等。我们就可以定义一系列的对象集合,通过相同的方式去执行我们的子规则

实战

首先我们的通道是分为交易笔数,交易时间,收银员等,所以我们选择策略模式,定义每种规则对应的行为,这样我们根据不同的规则类型去执行不同的行为 

image.png

先定义一个抽象类,指定每个规则行为,可以将公共的方法,放到抽取到抽象类中

 

java

复制代码

@Component @Slf4j public abstract class RouterStrategy{ @Resource OilUsersControlDAO oilUsersControlDAO; @Nullable public abstract RouterResult execute(RouterStrategyParam routerStrategyParam); /** * 获取规则路由状态, 1开始 2关闭 * @param routerStrategyParam 详情 * @return */ public Integer routerStatus(RouterStrategyParam routerStrategyParam){ OilUsersControlDO userControlByUserId = oilUsersControlDAO.getUserControlByUserId(routerStrategyParam.getBelongId()); if (Objects.isNull(userControlByUserId)) { return RouterStatusEnum.CLOSED.getCode(); } if (Objects.isNull(userControlByUserId.getRouterSwitch())) { return RouterStatusEnum.CLOSED.getCode(); } return userControlByUserId.getRouterSwitch(); } }

继承抽象类,实现自己的行为

image.png

以交易时间规则为例,定义bean名称,这样可以将所有的规则策略都交由枚举类进行管理

 

java

复制代码

/** * * @version RouterStrategyTradeTime.java, v 0.1 2024-03-12 11:01 */ @Component("tradeTime") @Slf4j @AllArgsConstructor public class RouterStrategyTradeTime extends RouterStrategy { private OilRuleConfigTradeTimeDAO oilRuleConfigTradeTimeDAO; private TradeAction tradeAction; @Override public RouterResult execute(RouterStrategyParam routerStrategyParam) { if (Objects.equals(routerStatus(routerStrategyParam), RouterStatusEnum.CLOSED.getCode())) { return null; } RouterResult routerResult = null; LogUtil.info(log, "RouterStrategyTradeTime.execute 开始执行规则 >> routerStrategyParam{}", routerStrategyParam); List<OilRuleConfigTradeTimeDO> ruleList = oilRuleConfigTradeTimeDAO.findRuleList(routerStrategyParam.getRuleId()); LogUtil.info(log, "RouterStrategyTradeTime.execute 获取规则详情 >> ruleList{}", ruleList); for (OilRuleConfigTradeTimeDO oilRuleConfigTradeTimeDO : ruleList) { // 交易金额不为空则判断 if (tradeAction.compareTradeAmount(oilRuleConfigTradeTimeDO.getMinPaymentAmount(), BigDecimal.ZERO) != BigDecimalResultConstants.COMPARE_RESULT) { if (tradeAction.compareTradeAmount(routerStrategyParam.getPayAmount(), oilRuleConfigTradeTimeDO.getMinPaymentAmount()) < BigDecimalResultConstants.COMPARE_RESULT) { continue; } if (tradeAction.compareTradeAmount(routerStrategyParam.getPayAmount(), oilRuleConfigTradeTimeDO.getMaxPaymentAmount()) > BigDecimalResultConstants.COMPARE_RESULT) { continue; } } // 交易笔数不为空则判断 if (oilRuleConfigTradeTimeDO.getTradeCount() != BigDecimalResultConstants.COMPARE_RESULT) { // 当前交易笔数大于规则交易笔数,则跳过 if (tradeAction.getTradeCount(routerStrategyParam.getBelongId(), routerStrategyParam.getStationId(), oilRuleConfigTradeTimeDO.getMerchantId()) >= oilRuleConfigTradeTimeDO.getTradeCount()) { continue; } } Integer timePeriodType = oilRuleConfigTradeTimeDO.getTimePeriodType(); if (Objects.equals(TimePeriodTypeEnum.WEEK.getCode(), timePeriodType)) { // 判断周 routerResult = compareWeek(oilRuleConfigTradeTimeDO, routerStrategyParam); } if (Objects.equals(TimePeriodTypeEnum.MONTH.getCode(), timePeriodType)) { // 判断月 routerResult= compareMonth(oilRuleConfigTradeTimeDO, routerStrategyParam); } if (Objects.equals(TimePeriodTypeEnum.DATE_PERIOD.getCode(), timePeriodType)) { // 判断日期段 routerResult= compareTimePeriod(oilRuleConfigTradeTimeDO, routerStrategyParam); } if (Objects.nonNull(routerResult)) { return routerResult; } } return null; } .... }

定义策略类枚举

 

java

复制代码

/** * 1.交易笔数、2.交易时间、3.收银员工 * * @version RuleConfigType.java, v 0.1 2024-03-11 18:02 */ public enum RouterConfigTypeEnum { TRADE_COUNT(1, "交易笔数","tradeCount"), TRADE_TIME(2, "交易时间","tradeTime"), CASHIER(3, "收银员","cashier"); private Integer configType; private String name; private String beanName; RouterConfigTypeEnum(Integer configType, String name, String beanName) { this.name = name; this.configType = configType; this.beanName = beanName; } public String getName() { return name; } public Integer getConfigType() { return configType; } public String getBeanName() { return beanName; } /** * 根据configType返回name */ public static String getBeanNameByConfigType(Integer configType) { for (RouterConfigTypeEnum routerConfigTypeEnum : RouterConfigTypeEnum.values()) { if (routerConfigTypeEnum.getConfigType().equals(configType)) { return routerConfigTypeEnum.getBeanName(); } } return ""; } }

执行路由规则

 

java

复制代码

try { LogUtil.info(log,"PayRouterServiceImpl.getRouter 开始获取支付规则路由 >> ruleByCurrentTime:{}",ruleByCurrentTime); //根据不同的规则类型获取对应的策略类 String beanNameByConfigType = RouterConfigTypeEnum.getBeanNameByConfigType(ruleByCurrentTime.getConfigType()); // 实例化对应的策略类 RouterStrategy routerStrategy = SpringUtil.getBean(beanNameByConfigType, RouterStrategy.class); routerStrategyParam.setRuleId(ruleByCurrentTime.getId()); // 执行策略类 RouterResult router = routerStrategy.execute(routerStrategyParam); // 规则策略执行器返回为空,则走规则默认支付路由 if (Objects.isNull(router)) { routerResult = payRouterServiceMapper.toRouterResultByOilPayRuleDO(ruleByCurrentTime); routerResult.setMerchantId(ruleByCurrentTime.getDefaultMerchantId()); return routerResult; } return router; }catch (Exception e){ LogUtil.error(log,"PayRouterServiceImpl.getRouter 获取路由规则失败>> 入参:{},e:",routerStrategyParam,e); }

到这里你会发现好像少了什么,组合模式怎么不见了,所以需要再改造一下,为什么需要组合模式,因为我们每个规则下还有不同的子规则,例如交易时间多种子规则 

image.png

定义子规则行为

 

java

复制代码

package com.oil.service.business.router.routerStrategy.combination; import com.oil.service.domain.param.pay.RouterStrategyParam; /** * * * @version SubruleAction.java, v 0.1 2024-03-30 12:02 */ public interface SubRuleAction { boolean execute(RouterStrategyParam routerStrategyParam); }

实现子规则行为 

image.png

 通过枚举管理每个子规则组合

 

java

复制代码

/** * @version RouterSubRuleEnum.java, v 0.1 2024-03-30 12:17 */ package com.oil.service.enums; import com.oil.service.business.router.routerStrategy.combination.SubRuleAction; import com.oil.service.business.router.routerStrategy.combination.TradeAmountAction; import com.oil.service.business.router.routerStrategy.combination.TradeCountAction; import java.util.ArrayList; import java.util.List; /** * * * @version RouterSubRuleEnum.java, v 0.1 2024-03-30 12:17 */ public enum RouterSubRuleEnum { TRADE_COUNT(1, "交易笔数"){ public List<SubRuleAction> getSubRule() { List<SubRuleAction> subRuleActions = new ArrayList<>(); subRuleActions.add(SpringUtil.getBean("TradeCountAction",SubRuleAction.class)); return subRuleActions; } }, TRADE_TIME(2, "交易时间"){ public List<SubRuleAction> getSubRule() { List<SubRuleAction> subRuleActions = new ArrayList<>(); subRuleActions.add(SpringUtil.getBean("TradeCountAction",SubRuleAction.class)); subRuleActions.add(SpringUtil.getBean("TradeAmountAction",SubRuleAction.class)); return subRuleActions; } }, CASHIER(3, "收银员"){ public List<SubRuleAction> getSubRule() { return new ArrayList<>(); } }; private Integer configType; private String name; RouterSubRuleEnum(Integer configType, String name) { this.configType = configType; this.name = name; } public Integer getConfigType() { return configType; } public String getName() { return name; } public abstract List<SubRuleAction> getSubRule(); public static List<SubRuleAction> getByConfigType(Integer configType) { for (RouterSubRuleEnum routerSubRuleEnum : RouterSubRuleEnum.values()) { if (routerSubRuleEnum.getConfigType().equals(configType)) { return routerSubRuleEnum.getSubRule(); } } return new ArrayList<>(); } }

获取规则策略的同时,传入组合好的子规则

 

java

复制代码

try { LogUtil.info(log,"PayRouterServiceImpl.getRouter 开始获取支付规则路由 >> ruleByCurrentTime:{}",ruleByCurrentTime); String beanNameByConfigType = RouterConfigTypeEnum.getBeanNameByConfigType(ruleByCurrentTime.getConfigType()); RouterStrategy routerStrategy = SpringUtil.getBean(beanNameByConfigType, RouterStrategy.class); routerStrategyParam.setRuleId(ruleByCurrentTime.getId()); RouterResult router = routerStrategy.execute(routerStrategyParam // 获取子规则集合 ,RouterSubRuleEnum.getByConfigType(ruleByCurrentTime.getConfigType())); // 规则策略执行器返回为空,则走规则默认支付路由 if (Objects.isNull(router)) { routerResult = payRouterServiceMapper.toRouterResultByOilPayRuleDO(ruleByCurrentTime); routerResult.setMerchantId(ruleByCurrentTime.getDefaultMerchantId()); return routerResult; } return router; }catch (Exception e){ LogUtil.error(log,"PayRouterServiceImpl.getRouter 获取路由规则失败>> 入参:{},e:",routerStrategyParam,e); }

交易时间规则类,执行子规则判断

 

java

复制代码

public RouterResult execute(RouterStrategyParam routerStrategyParam, List<SubRuleAction> subRuleActionList) { if (Objects.equals(routerStatus(routerStrategyParam), RouterStatusEnum.CLOSED.getCode())) { return null; } RouterResult routerResult = null; LogUtil.info(log, "RouterStrategyTradeTime.execute 开始执行规则 >> routerStrategyParam{}", routerStrategyParam); List<OilRuleConfigTradeTimeDO> ruleList = oilRuleConfigTradeTimeDAO.findRuleList(routerStrategyParam.getRuleId()); LogUtil.info(log, "RouterStrategyTradeTime.execute 获取规则详情 >> ruleList{}", ruleList); for (OilRuleConfigTradeTimeDO oilRuleConfigTradeTimeDO : ruleList) { // 子规则判断 AtomicBoolean result = new AtomicBoolean(true); subRuleActionList.forEach(subRuleAction -> result.set(subRuleAction.execute(routerStrategyParam))); // 校验未通过则跳过 if (!result.get()) { continue; } Integer timePeriodType = oilRuleConfigTradeTimeDO.getTimePeriodType(); if (Objects.equals(TimePeriodTypeEnum.WEEK.getCode(), timePeriodType)) { // 判断周 routerResult = compareWeek(oilRuleConfigTradeTimeDO, routerStrategyParam); } if (Objects.equals(TimePeriodTypeEnum.MONTH.getCode(), timePeriodType)) { // 判断月 routerResult= compareMonth(oilRuleConfigTradeTimeDO, routerStrategyParam); } if (Objects.equals(TimePeriodTypeEnum.DATE_PERIOD.getCode(), timePeriodType)) { // 判断日期段 routerResult= compareTimePeriod(oilRuleConfigTradeTimeDO, routerStrategyParam); } if (Objects.nonNull(routerResult)) { return routerResult; } } return null; }

总结

使用设计模式主要是为了扩展性和规范性,通过不同模式之间进行设计,可以很好的规范我们对某个功能模块的开发行为,就比如我们之后可能还需要有新的子规则,只需要再实现新的子规则类,添加到对应的组合集合中就可以完成对子规则的添加,我们既不需要理解原有代码的逻辑,也不需要改动原有代码,只需要遵从规定的规范就可以快速的实现,何乐而不为

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值