面向对象编程内功心法系列十五(聊一聊策略模式)

138 阅读5分钟

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);   
   } 
    
}