写在前面的话:项目基本完结了,复盘项目时,突发奇想,负责的模块居然符合策略模式的使用场景,所以引入策略模式,重构了下负责的模块,在此记录一下。
1. 业务场景
用户购票过程中,可以参加符合条件的优惠活动,并根据不同类型活动,计算优惠金额和支付金额,活动类型包括:
1. 立减活动
2. 立减到活动
3. 满减活动
2. 传统处理/策略模式
2.1 传统模式
if (KaActivityCinemaOrderEnum.NOW_REDUCTION.getCode().equals(activityType)) {
// 立减活动计算逻辑
} else if (KaActivityCinemaOrderEnum.NOW_REDUCTION_TO.getCode().equals(activityType)) {
// 立减到活动计算逻辑
} else {
// 满减活动计算逻辑
}
2.2 策略模式
CalculationDiscountContext context = new CalculationDiscountContext();
Long discount = context.getActivityDiscountAmount(String.valueOf(activityType), activityCode, activityType, ticketOrderNo, seatCount);
2.3 什么是策略模式
https://blog.youkuaiyun.com/u012401711/article/details/524633479 (喜欢这类大神的讲解,形象便于理解,有兴趣的同学可以看看)
3. SpringBoot引入策略模式
-
创建抽象类或接口类
@Component public abstract class CalculationDiscountAmount { @Autowired public KaActivityCinemaOrderDao kaActivityCinemaOrderDao; @Autowired public KaActivityFullSubtractDao kaActivityFullSubtractDao; public static CalculationDiscountAmount calculationDiscountAmount; /** * 在方法上加上注解@PostConstruct,保证Bean初始化前已经装配了属性(注:Bean初始化包括,实例化Bean,并装配Bean的属性(依赖注入)) */ @PostConstruct public void init() { calculationDiscountAmount = this; } public abstract Long getActivityDiscountAmount(Integer activityCode, Integer activityType, String ticketOrderNo, Integer count); }
-
创建实现类并重写抽象方法
@Component public class FullReductionDiscountImpl extends CalculationDiscountAmount { private Logger logger = LoggerFactory.getLogger(TicketSupplementActivityServiceImpl.class); @Override public Long getActivityDiscountAmount(Integer activityCode, Integer activityType, String ticketOrderNo, Integer count) { try { // 优惠计算逻辑 } catch (Exception e) { logger.error("计算活动优惠金额异常,activityCode" + activityCode + ",activityType" + activityType + ",ticketOrderNo" + ",count" + count, e); throw new ArithmeticException(); } return result; } }
-
创建计算优惠金额容器
@Component public class CalculationDiscountContext { @Autowired private final Map<String, CalculationDiscountAmount> map = new ConcurrentHashMap<>(); public CalculationDiscountContext() { this.map.clear(); map.put(String.valueOf(KaActivityCinemaOrderEnum.FULL_REDUCTION.getCode()), new FullReductionDiscountImpl()); } public Long getActivityDiscountAmount(String poolId, Integer activityCode, Integer activityType, String ticketOrderNo, Integer count) { return map.get(poolId).getActivityDiscountAmount(activityCode, activityType, ticketOrderNo, count); } }
-
测试用例
@Test public void CalculationDiscountContext() { String activityType = "3"; CalculationDiscountContext context = new CalculationDiscountContext(); context.getActivityDiscountAmount(activityType, 2210, Integer.valueOf(activityType), "81729381298379", 2); }
4. 策略模式优势
1. 耦合性大大降低,代码不再堆积;
2. 在有多种算法相似的情况下,解决 if...else 所带来的复杂和难以维护问题;
3. 具有良好的扩张性,如果活动增加时,只需要增加实现类和实现逻辑即可。
5. 问题与解决方法
1. 传统方式通过Spring管理对象
解决方法:由于Spring管理对象时,由于一个抽象类有多个实现会报异常。所以使用了ConcurrentHashMap,根据不同类型,获取不同的实例对象,也可以使用注解的方式。
2. @Component注解类中@Autowired注入对象为null
解决方法:在方法上加上注解@PostConstruct,保证Bean初始化前已经装配了属性(注:Bean初始化包括,实例化Bean,并装配Bean的属性(依赖注入))
(https://blog.youkuaiyun.com/z595746437/article/details/80340836)