前言
最近接触了优惠券相关的业务,如果是以前,我第一时间想到的就是if_else开始套,这样的话耦合度太高了,如果后期添加或者删除优惠券,必须直接修改业务代码,不符合开闭原则,这时候就可以选择我们的策略模式(Strategy Pattern)实现解耦,很好的解决上述问题,消除 if-else、switch 多重判断 可以有效应对代码的复杂性,十分优雅!
概念引入
和上期的建造者模式类似,策略模式也有三个重要元素:
- 环境类:用于与策略接口交互的类。
- 策略接口:定义所有支持的算法的公共接口。
- 具体策略类:实现策略接口的具体算法。
具体案例(针对优惠券的案例)
我们都知道不同优惠券有不同的优惠力度,非常契合策略模式,本案例将结合策略模式和Spring特性来展示:
1.创建优惠券的策略接口
/*
* 优惠券就是一个很好的策略模式的案例
* */
public interface DiscountStrategy {
//优惠券折扣就可以抽象到接口中
Double discount(Double price);
//用于标识不同的折扣力度
String mark();
}
我们通过接口来管理优惠券,内部有负责折扣的抽象方法,具体折扣计算由实现类各自负责;还有负责标识的 mark方法。
2.优惠券的具体实现类
/*
* 八折的折扣类,首先要实现之前的折扣接口,然后重写方法
* 利用Spring的特性,将折扣实现类注入IOC容器
* */
@Component
public class Discount8Strategy implements DiscountStrategy{
//8折折扣方法
@Override
public Double discount(Double price) {
return price*0.8;
}
@Override
public String mark() {
return "1";
}
}
/*
* 九折的折扣类,首先要实现之前的折扣接口,然后重写方法
* 利用Spring的特性,将折扣实现类注入IOC容器
* */
@Component
public class Discount9Strategy implements DiscountStrategy{
//9折折扣方法
@Override
public Double discount(Double price) {
return price*0.9;
}
@Override
public String mark() {
return "2";
}
}
这里我添加了两种折扣类,分别是8折和9折,同时利用了@Component将二者注入到Spring的IOC容器中,后续将配合Spring完成整个策略模式的实现
3.优惠券工厂+Spring生命周期使用
/*
* 这里利用Spring的生命周期,实现InitializingBean方法,在bean初始化这一个阶段做一些处理,实现策略模式
* */
@Component
public class DiscountStrategyFactory implements InitializingBean {
//获取到ApplicationContext的实例,可以通过这个获取到想要的类实例
@Autowired
private ApplicationContext applicationContext;
//用来真正存储 (折扣力度标识,对应折扣实例)
public final HashMap<String,DiscountStrategy> discountStrategies = new HashMap<>();
//根据标识的mark选择对应的折扣实例,后续可以进行折扣
public DiscountStrategy chooseDiscount(String mark)
{
return discountStrategies.get(mark);
}
@Override
public void afterPropertiesSet() throws Exception {
//因为到AfterPropertiesSet这个方法的时候,当前Bean的属性已经设置完成,所以我们可以获取到DiscountStrategy接口的实现类
//利用Map这个数据类型,完成策略模式
//使用了GetBeansOfType方法直接返回map类型。key是类的名字,value是对应的实例
Map<String,DiscountStrategy> discountStrategyMap = applicationContext.getBeansOfType(DiscountStrategy.class);
//利用stream流的方式遍历,扩充discountStrategies
discountStrategyMap.forEach((key,value)-> discountStrategies.put(value.mark(),value));
}
}
这里策略工厂类实现了InitializingBean接口,是利用了Spring生命周期中的AfterPropertiesSet方法,也就是在Bean初始化后这一个阶段。首先注入ApplicationContext实例,然后利用它调用GetBeanOfType得到(类名,对应实例)这一个Map对象,再利用Stream流进行处理得到了一个Key是mark,value是对应折扣实例的一个map,后续我们就可以使用这个map进行策略模式的使用。
4.具体使用
折扣成功,这样业务中就可以利用类似的方式处理优惠券,是不是比if_else更加优雅,同时,后期遇到增删改的情况,是不是就符合了开闭原则。
总结
入门了策略模式,我们最后看看他的一些优势:
- 封装性:策略模式通过将算法封装在独立的类中,使得算法的变化不会影响到使用算法的客户端。
- 可替换性:可以在运行时选择不同的算法来执行,而不需要修改客户端的代码。
- 可扩展性:新增算法时,只需新增一个具体的策略类即可,符合开闭原则。