1.引子
实际项目开发中,我们经常会碰到这样的场景。举个例子,我自己当前的项目中,简单介绍一下,我们是社保费项目(公司给我们购买五险一金中的五险,即是通过社保费系统完成)。因为疫情的关系,业务上有一个减免的需求要实现,针对不同的申报方式类型,对不同的险种(工伤、养老、失业、医疗)进行减免。写成伪代码,大概是这样的
// 封装社保费减免业务逻辑
public void decrease(String type){
if(type.equals("自行申报")){
// 自行申报减免逻辑
}else if(type.equals("补交申报")){
// 补缴申报减免逻辑
}else if(type.equals("手工录入申报")){
// 手工录入申报减免逻辑
}else if(type.equals("xxx申报")){
// xxx申报减免逻辑
}......
}
业务上申报类型,大概有10几种,即意味着decrease方法中将有10几个if...else分支逻辑。编程老司机如你,一定不喜欢这样的代码实现,CR的时候一定不会让通过对吧。不要问为什么?因为
- 这样的代码,可读性差,一堆if...else分支判断,中间还夹杂着具体的业务逻辑代码实现
- 这样的代码,耦合性高,比如违背了单一职责原则
- 这样的代码,扩展性、可维护性差,比如违背了开闭原则
那你可能要问了,这也不好,那也不好,究竟该如何是好呢?答案是策略模式,策略模式可以很好的帮助我们解决分支逻辑多的问题。至于什么是策略模式,以及如何实现使用策略模式,让我们一起来看一下吧
2.案例
2.1.如何实现策略模式
首先,让我们来搞清楚什么是策略模式?GoF设计模式中,它是被这么定义的:定义一族算法类,将每个算法封装起来,算法与算法之间可以相互替换,且算法的变化与替换独立于客户端调用者。
用人话说,即是基于接口编程,接口实现之间可以相互替换,且实现了与接口调用者解耦。
你看这就是策略模式,理解起来并不难。
那我们要如何实现一个策略模式呢?
策略模式在实现上,我们需要分为三个部分
- 策略的定义,即接口与接口实现
- 策略的创建,利用工厂模式实例化策略
- 策略的使用,客户端调用
你看,根据上面三个步骤,实现策略模式也不难,so easy!对吧。接下来,为了方便更多朋友理解策略模式的实现,我们不用开篇的案例,换一个大家都熟悉的案例,电商促销折扣的案例,它的伪代码是这样的
public class OrderServiceImpl implements OrderService{
/**
*实现订单折扣:普通订单、促销订单、团购订单等
*/
public BigDecimal discount(Order order){
BigDecimal amount = new BigDecimal("0");
String type = order.getType();
if(type.equals("普通订单")){
// 普通订单折扣代码实现
}else if(type.equals("促销订单")){
// 促销订单折扣代码实现
}else if(type.equals("团购订单")){
// 团购订单折扣代码实现
}
return amount;
}
}
2.2.策略模式案例实现
2.2.1.策略定义
2.2.1.1.折扣接口
/**
* 折扣接口
* @author ThinkPad
* @version 1.0
* @date 2021/4/4 10:50
*/
public interface DiscountStrategy {
/**
* 折扣算法
* @param order
* @return
*/
BigDecimal discount(Order order);
}
2.2.1.2.普通订单折扣实现
/**
* 普通订单折扣算法实现
* @author ThinkPad
* @version 1.0
* @date 2021/4/4 10:51
*/
@Slf4j
public class NormalDiscountStrategy implements DiscountStrategy{
/**
* 折扣算法
* @param order
* @return
*/
@Override
public BigDecimal discount(Order order) {
BigDecimal amount = new BigDecimal("0");
log.info("普通订单折扣算法实现,订单类型:{}", order.getType());
return amount;
}
}
2.2.1.3.促销订单折扣实现
/**
*促销订单折扣算法实现
* @author ThinkPad
* @version 1.0
* @date 2021/4/4 10:54
*/
@Slf4j
public class PromotionDiscountStrategy implements DiscountStrategy {
/**
* 折扣算法
* @param order
* @return
*/
@Override
public BigDecimal discount(Order order) {
BigDecimal amount = new BigDecimal("0");
log.info("促销订单折扣算法实现,订单类型:{}", order.getType());
return amount;
}
}
2.2.1.4.团购订单折扣实现
/**
* 团购订单折扣算法实现
* @author ThinkPad
* @version 1.0
* @date 2021/4/4 10:55
*/
@Slf4j
public class GrouponDiscountStrategy implements DiscountStrategy {
/**
* 折扣算法
* @param order
* @return
*/
@Override
public BigDecimal discount(Order order) {
BigDecimal amount = new BigDecimal("0");
log.info("团购订单折扣算法实现,订单类型:{}", order.getType());
return amount;
}
}
2.2.2.策略创建
/**
* 折扣算法实例化工厂
* @author ThinkPad
* @version 1.0
* @date 2021/4/4 10:57
*/
public class DiscountStrategyFactory {
/**
* 查表法,存储折扣算法实例
*/
private static final Map<String, DiscountStrategy> strategies
= new HashMap<String,DiscountStrategy>();
static {
strategies.put("普通订单",new NormalDiscountStrategy());
strategies.put("促销订单",new PromotionDiscountStrategy());
strategies.put("团购订单",new GrouponDiscountStrategy());
}
/**
* 根据订单类型,获取对应的折扣算法实例
* @param type
* @return
*/
public static DiscountStrategy getDiscountStrategy(String type){
return strategies.get(type);
}
}
2.2.4.策略使用
借助策略模式,我们成功去除了discount方法中的if...else分支逻辑,这里去除if...else分支逻辑,有一个小技巧
- 因为策略算法实现,是无状态的,因此我们可以将不同类型的折扣算法提前实例化,存储到Map容器中
- 客户端调用的折扣算法的时候,根据订单类型,从Map容器中获取相应的折扣算法实例,完成折扣业务处理
这个小技巧,叫做查表法,你可以具体关注一下DiscountStrategyFactory工厂中的实现,我就不多说了。通过策略模式,我们实现了
- 去除if...else分支逻辑,让业务代码可读性更好
- 去除if...else分支逻辑,提升了业务的扩展性,可维护性
- 当需要增加新的折扣算法,只需要增加一个DiscountStrategy接口的实现即可,不再违背开闭原则
public class OrderServiceImpl implements OrderService{
/**
*实现订单折扣:普通订单、促销订单、团购订单等
*/
public BigDecimal discount(Order order){
// 订单类型
String type = order.getType();
// 根据订单类型,获取折扣策略实例,完成折扣
DiscountStrategy strategy =
DiscountStrategyFactory.getDiscountStrategy(type);
return strategy.discount(order);
}
}