用设计模式解决电商项目的折扣优惠类型业务

文章目录

  • 一. 问题背景
  • 二. 前言
  • 三. 解决方案
    • 3.1 没有用设计模式
    • 3.2 用设计模式
      • 3.2.1 设计接口
      • 3.2.2 设计折扣计算实现类的选择器
      • 3.2.3 设计折扣计算基类
      • 3.2.4 折扣优惠实现类
        • 3.2.4.1 默认折扣
        • 3.2.4.2 满减折扣
        • 3.2.4.3 买N件第N+1件打折
  • 四. 源码

一. 问题背景

场景:电商项目中的折扣优惠类型,有若干种,用策略模式做业务逻辑,提高系统的扩展性。

注意:笔者采用lombok写代码,所以代码块中会少了很多getter、setter、构造函数。

技术栈:SpringBoot 2.x + Java 8

用到的设计模式:策略模式+模板方法

二. 前言

本文只贴关键代码,有些地方不用过于纠结,文末提供全部源码

三. 解决方案

3.1 没有用设计模式

折扣实体类:

@Data
public class DiscountDTO {
    /**
     * 折扣id
     */
    private Integer id;
    /**
     * 折扣码
     */
    private String code;
    /**
     * 折扣类型(比如0:满件打折,1:满额打折,2:第N+1件打折)
     */
    private Integer type;
}

业务逻辑:

public class TestServiceImpl {
    public void test(DiscountDTO discountDTO) {
       if(discountDTO.getType == 0) {
           service();
           serviceA1();
       } else if (discountDTO.getType == 1) {
           service();
           serviceA2();
       } else if (discountDTO.getType == 2) {
           service();
           serviceA3();
       }
    }
}

缺点:

  • 每增加一种折扣优惠类型,都需要增加if-else,然后会有过多if-else堆叠在一起
  • 如上代码块所示,每种优惠类型都会有相同的逻辑(比如service()),每个if-else都需要添加,后期维护也很麻烦

3.2 用设计模式

3.2.1 设计接口

每个优惠类型的计算过程,都是一组入参,然后经过计算,得出优惠结果,也就是出参的结果报文是一样的(即计算都是得出优惠了多少钱,售价是多少,实际支付是多少)。那么我们设计一个接口,抽象出来,具体实现交给各自的优惠类型实现类去实现细节。

/**
 * @description: 折扣码的计算结果报文
 * @author: ganzalang
 **/
@Data
public class DiscountCalculateDTO {
    /**
     * 总优惠金额
     */
    private BigDecimal discountAmount;
    /**
     * 售价
     */
    private BigDecimal saleAmount;
    /**
     * 实际支付金额
     */
    private BigDecimal payAmount;

}

3.2.2 设计折扣计算实现类的选择器

通过选择器,选出具体的折扣计算实现类,这样就可以去除了大量if-else堆叠

/**
 * @description: 折扣计算工厂选择器
 * @author: ganzalang
 */
@Service
@Slf4j
public class DiscountCalculatorSelector {

    /**
     * 折扣类型(有满件减价、满额减价、第N件打折、捆绑销售、搭售等等)
     * 用Map来做多实现类注入,key是beanName,value是beanName对应的bean
     */
    @Autowired
    private Map<String, DiscountCalculator> discountCalculatorMap;

    public DiscountCalculator create(DiscountTypeEnum discountType) {
        return discountCalculatorMap.get(discountType.getBeanName());
    }
}
/**
 * @description: 折扣计算工厂选择器
 * @author: ganzalang
 */
@Getter
@AllArgsConstructor
public enum DiscountTypeEnum {
    /**
     * 默认无折扣
     */
    DEFAULT(0, "defaultDiscountCalculator"),
    /**
     * 满件折扣
     */
    FULL_PIECE_DISCOUNT(1, "fullPieceDiscountCalculator"),
    /**
     * 买N件第N+1件打折
     */
    MULTI_DISCOUNT(2, "multiDiscountCalculator");




    private Integer type;
    private String beanName;

    public static DiscountTypeEnum getByType(Integer type){
        for (DiscountTypeEnum value : DiscountTypeEnum.values()) {
            if(value.getType().equals(type)){
                return value;
            }
        }
        return DEFAULT;
    }
}

3.2.3 设计折扣计算基类

公共逻辑写具体实现,各自特性的逻辑做抽象,交由实现类做具体实现。 这样就抽取了各种优惠类型的共同逻辑,具体细节交给各自实现类,这里用了模板方法设计模式

/**
 * @description: 折扣计算基类(公共逻辑写具体实现,各自特性的逻辑做抽象,交由实现类做具体实现)
 * @author: ganzalang
 */
@Slf4j
public abstract class AbstractDiscountCalculator implements DiscountCalculator{

    @Override
    public DiscountCalculateDTO calculate() {
        DiscountCalculateDTO discountCalculateDTO = new DiscountCalculateDTO();

        //对各个渠道的折扣码进行特殊处理
        Boolean flag = doSomething();

        //如果需要继续计算,则计算结果,否则直接返回
        if(flag){
            serviceA();
            serviceB();
        }

        return discountCalculateDTO;
    }

    public Boolean doSomething() {
        // 做逻辑处理
        log.info("相同逻辑的方法");
        return true;
    }

    /**
     * 每种折扣类型都有自己的具体实现
     */
    protected abstract void serviceA();

    /**
     * 每种折扣类型都有自己的具体实现
     */
    protected abstract void serviceB();

}

3.2.4 折扣优惠实现类

3.2.4.1 默认折扣

/**
 * @description: 默认折扣,不使用折扣
 * @author: ganzalang
 **/
@Slf4j
@Service
public class DefaultDiscountCalculator extends AbstractDiscountCalculator{
    @Override
    protected void serviceA() {
        log.info("DefaultDiscountCalculator's serviceA");
    }

    @Override
    protected void serviceB() {
        log.info("DefaultDiscountCalculator's serviceB");
    }
}

3.2.4.2 满减折扣

/**
 * @description: 满件减价
 * @author: ganzalang
 **/
@Service
@Slf4j
public class FullPieceDiscountCalculator extends AbstractDiscountCalculator{
    @Override
    protected void serviceA() {
        log.info("FullPieceDiscountCalculator's serviceA");
    }

    @Override
    protected void serviceB() {
        log.info("FullPieceDiscountCalculator's serviceB");
    }
}

3.2.4.3 买N件第N+1件打折

/**
 * @description: 满N件,第N+1件打折
 * @author: ganzalang
 **/
@Slf4j
@Service
public class MultiDiscountCalculator extends AbstractDiscountCalculator{
    @Override
    protected void serviceA() {
        log.info("MultiDiscountCalculator's serviceA");
    }

    @Override
    protected void serviceB() {
        log.info("MultiDiscountCalculator's serviceB");
    }
}

四. 源码

前往gitee页面下载

上面提供的gitee工程目录采用多模块构成,本文的源码在gmall-design-strategy模块

你可能感兴趣的:(解决方案,设计模式)