行为模式之策略模式的案例示范

1. 策略模式介绍

在这里插入图片描述

策略模式是一种行为模式,也是替代大量ifelse的利器。它所能解决的场景,一般是具有同类可替代的行为逻辑算法场景。比如,不同类型的交易方式(信用卡、支付宝、微信)、生成唯一ID策略(UUID、DB自增、DB+Redis、雪花算法、Leaf算法)等,都可以使用策略模式进行包装,供给外部使用。

策略模式有点像三国演义中诸葛亮给刘关张的锦囊:

  • 第一个锦囊:见乔国老,并把刘备娶亲的事情搞得东吴人尽皆知。
  • 第二个锦囊:用曹操打荆州的谎言骗跑在温柔乡的刘备回去。
  • 第三个锦囊:让孙夫人摆平东吴的追兵,她是孙权妹妹,东吴将领惧她三分。

2. 案例场景模拟

在这里插入图片描述

在本案例中我们模拟在购物时使用的各种类型的优惠券(满减、直减、折扣、n元购)。

这个场景几乎也是大家的一个日常购物省钱渠道,我们希望找一些优惠券,让购买的商品更加实惠,而且到了大促会有更多的优惠券需要计算哪些商品一起购买更加优惠。

这样的场景用户用起来还是蛮爽的,但是随着产品的不断迭代,对于程序员开发还是不容易的。因为这里包括了很多的规则和优惠逻辑,所以我们模拟其中的一个计算优惠的方式,使用策略模式来实现。

3. 用一坨坨代码实现

对于优惠券的设计最初可能非常简单,就是一个金额的折扣,也没有现在这么多种类型。所以如果没有这样场景的经验的话,往往设计上也是非常简单的。但随着产品功能的不断迭代,如果程序最初设计的不具有很好的扩展性,那么往后就会越来越混乱。

工程结构

org.itstack.demo.design
----CouponDiscountService.java
  • 一坨坨工程的结构很简单,也是最直接的面向过程开发方式。

代码实现

public class CouponDiscountService {
    public double discountAmount(int type, double typeContent, double skuPrice, double typeExt) {
    // 1. 直减券
    if (1 == type) {
    	return skuPrice - typeContent;
    }
    // 2. 满减券
    if (2 == type) {
        if (skuPrice < typeExt) return skuPrice;
        return skuPrice - typeContent;
    }
    // 3. 折扣券
    if (3 == type) {
    	return skuPrice * typeContent;
    }
    // 4. n元购
    if (4 == type) {
    	return typeContent;
    }
    return 0D;
    }
}
  • 入参包括:优惠券类型type、优惠券金额typeContent、商品金额skuPrice,因为有些优惠券是满多少减多少,所以增加typeExt,这也是方法的不好扩展的问题。
  • 最后整个的方法体对优惠券抵扣金额的实现,最开始可能是一个最简单的优惠券,后面随着产品功能的增加,不断的扩展if语句,实际的代码比这个要多得多。

4. 策略模式重构代码

与上面面向流程式的开发这里会使用设计模式,优化代码结构,增强整理的扩展性。

工程结构

org.itstack.demo.design
----event
	----MJCouponDiscount.java
	----NYGCouponDiscount.java
	----ZJCouponDiscount.java
	----ZKCouponDiscount.java
----Context.java
----ICouponDiscount.java

策略模式模型结构

在这里插入图片描述

  • 整体的结构模式并不复杂,主要体现的不同类型的优惠券在计算优惠券方式的不同计算策略。
  • 这里包括一个接口类ICouponDiscount,以及4种优惠券类型的实现方式。
  • 最后提供了策略模式的上下控制类处理整体的策略服务。

代码实现

优惠券接口

public interface ICouponDiscount<T> {
    /**
    * 优惠券金额计算
    * @param couponInfo 券折扣信息:直减、满减、折扣、N元购
    * @param skuPrice sku金额
    * @return 优惠后金额
    */
    BigDecimal discountAmount(T couponInfo, BigDecimal skuPrice);
}
  • 定义了优惠券折扣接口,也增加了泛型用于不同类型的接口可以传递不同的类型参数。
  • 接口中包含商品金额以及出参返回最终折扣后的金额,在实际开发中会比现在的接口参数多一些,但核心逻辑是这些。

满减接口实现

public class MJCouponDiscount implements ICouponDiscount<Map<String,String>> {
    /**
    * 满减计算
    * 1. 判断满足x元后-n元
    * 2. 最低支付金额1元
    */
    public BigDecimal discountAmount(Map<String,String> couponInfo,BigDecimal skuPrice) {
        String x = couponInfo.get("x");
        String o = couponInfo.get("n");
        // 小于条件金额的,直接返回原价
        if (skuPrice.compareTo(new BigDecimal(x)) < 0) return skuPrice;
        // 减去优惠金额判断
        BigDecimal discountAmount = skuPrice.subtract(new BigDecimal(o));
        if (discountAmount.compareTo(BigDecimal.ZERO) < 1) return BigDecimal.ONE;
        return discountAmount;
    }
}

直减接口实现

public class ZJCouponDiscount implements ICouponDiscount<Double> {
    /**
    * 直减计算
    * 1. 商品价格-优惠价格
    * 2. 最低支付金额1元
    */
    public BigDecimal discountAmount(Double couponInfo, BigDecimal skuPrice) {
        BigDecimal discountAmount = skuPrice.subtract(new BigDecimal(couponInfo));
        if (discountAmount.compareTo(BigDecimal.ZERO) < 1) return BigDecimal.ONE;
        return discountAmount;
    }
}

折扣接口实现

public class ZKCouponDiscount implements ICouponDiscount<Double> {
    /**
    * 折扣计算
    * 1. 商品价格X折扣比例
    * 2. 保留两位小数
    * 3. 最低支付金额1元
    */
    public BigDecimal discountAmount(Double couponInfo, BigDecimal skuPrice) {
        BigDecimal discountAmount = skuPrice.multiply(new BigDecimal(couponInfo)).setScale(2, BigDecimal.ROUND_HALF_UP);
        if (discountAmount.compareTo(BigDecimal.ZERO) < 1) return BigDecimal.ONE;
        return discountAmount;
    }
}

N元购

public class NYGCouponDiscount implements ICouponDiscount<Double> {
    /**
    * n元购计算
    * 1. 无论原价多少都固定金额购买
    */
    public BigDecimal discountAmount(Double couponInfo, BigDecimal skuPrice) {
    	return new BigDecimal(couponInfo);
    }
}
  • 以上是4种不同类型的优惠券计算折扣金额的策略方式。

策略控制类

public class Context<T> {
    private ICouponDiscount<T> couponDiscount;
    
    public Context(ICouponDiscount<T> couponDiscount) {
    	this.couponDiscount = couponDiscount;
    }
    public BigDecimal discountAmount(T couponInfo, BigDecimal skuPrice) {
    	return couponDiscount.discountAmount(couponInfo, skuPrice);
    }
}
  • 策略模式的控制类主要是外部可以传递不同的策略实现,在通过统一的方法执行优惠策略计算。
  • 另外这里也可以包装成map结构,让外部只需要对应的泛型类型即可使用对应的服务。

测试验证

编写测试类

@Test
public void test_zj() {
    // 直减:100-10
    Context<Double> context = new Context<Double>(new ZJCouponDiscount());
    BigDecimal discountAmount = context.discountAmount(10D, new BigDecimal(100));
    logger.info("测试结果:直减优惠后金额 {}", discountAmount);
    
    // 满减:满100-10
    Context<Map<String,String>> context = new Context<Map<String,String>> (new MJCouponDiscount());
    Map<String,String> mapReq = new HashMap<String, String>();
    mapReq.put("x","100");
    mapReq.put("n","10");
    BigDecimal discountAmount = context.discountAmount(mapReq, new BigDecimal(100));
    logger.info("测试结果:满减优惠后金额 {}", discountAmount);
    
    // 折扣9折
    Context<Double> context = new Context<Double>(new ZKCouponDiscount());
    BigDecimal discountAmount = context.discountAmount(0.9D, new BigDecimal(100));
    logger.info("测试结果:折扣9折后金额 {}", discountAmount);
    
    // n元购
    Context<Double> context = new Context<Double>(new NYGCouponDiscount());
    BigDecimal discountAmount = context.discountAmount(90D, new BigDecimal(100));
    logger.info("测试结果:n元购优惠后金额 {}", discountAmount);
}

测试结果

15:43:22.035 [main] INFO org.itstack.demo.design.test.ApiTest - 测试结果:直减优惠后金额 90
15:43:42.695 [main] INFO org.itstack.demo.design.test.ApiTest - 测试结果:满减优惠后金额 90
15:44:05.602 [main] INFO org.itstack.demo.design.test.ApiTest - 测试结果:折扣9折后金额 90.00
15:44:24.700 [main] INFO org.itstack.demo.design.test.ApiTest - 测试结果:n元购优惠后金额 90    
    
Process finished with exit code 0
  • 以上分别验证了不同类型的优惠券的优惠策略,测试结果满足我们的预期。

5. 总结

  • 以上的策略模式案例相对来说并不复杂,主要的逻辑都是体现在关于不同种类优惠券的计算折扣策略上。结构也相对简单,在实际的开发中这样的设计模式也是非常常见的。另外,这样的设计与命令模式、适配器模式结构相似,但思路是有差异的。
  • 通过策略模式的使用可以把我们方法中的if语句优化掉,满足设计原则中的隔离性和扩展性。
  • 策略模式、适配器模式、组合模式在一些结构上比较相似,但是每一个模式是有自己的逻辑特点的,在后续过程中需要去慢慢体会和实现。
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值