场景:电商项目中的折扣优惠类型,有若干种,用策略模式做业务逻辑,提高系统的扩展性。
注意:笔者采用lombok写代码,所以代码块中会少了很多getter、setter、构造函数。
技术栈:SpringBoot 2.x + Java 8
用到的设计模式:策略模式+模板方法
本文只贴关键代码,有些地方不用过于纠结,文末提供全部源码
折扣实体类:
@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
都需要添加,后期维护也很麻烦
每个优惠类型的计算过程,都是一组入参,然后经过计算,得出优惠结果,也就是出参的结果报文是一样的(即计算都是得出优惠了多少钱,售价是多少,实际支付是多少)。那么我们设计一个接口,抽象出来,具体实现交给各自的优惠类型实现类去实现细节。
/**
* @description: 折扣码的计算结果报文
* @author: ganzalang
**/
@Data
public class DiscountCalculateDTO {
/**
* 总优惠金额
*/
private BigDecimal discountAmount;
/**
* 售价
*/
private BigDecimal saleAmount;
/**
* 实际支付金额
*/
private BigDecimal payAmount;
}
通过选择器,选出具体的折扣计算实现类,这样就可以去除了大量
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;
}
}
公共逻辑写具体实现,各自特性的逻辑做抽象,交由实现类做具体实现。 这样就抽取了各种优惠类型的共同逻辑,具体细节交给各自实现类,这里用了模板方法设计模式
/**
* @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();
}
/**
* @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");
}
}
/**
* @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");
}
}
/**
* @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
模块