策略模式介绍

策略模式是一种行为模式,也是替代if…else的利器。他能解决的场景一般包括具有同类可替代的行为逻辑算法,比如不同的交易方式等。

简单使用场景

模拟购物商品时使用的各类优惠券,包括满减、直减、折扣和N元购等。

这个场景大家都不会陌生,在购买商品时使用优惠券。在大促时,会有更多的优惠券,需要计算哪些商品一起购买更加优惠。实现此功能并不容易,因为里面包括了很多的规则和优惠逻辑,可以模拟其中一种计算优惠的方式,使用策略模式实现。

当然对于优惠的设计,最初可能非常简单,只是一个金额的抵扣,可没有很多的类型,所以虽然设计起来非常简单,但随着产品功能的不断迭代,一旦程序不具备很好的扩展性,就会越来越混乱。

策略模式实现商品优惠使用场景

image-20211229105414551

整体结构并不复杂,主要体现不同类型的优惠券的不同计算策略,包括一个接口类(ICoupondiscount)和四种优惠券的实现方式。最后提供了策略模式的上下控制类,用于处理整体的策略服务。

优惠券接口

1
2
3
4
5
package com.bestrook.design;
import java.math.BigDecimal;
public interface ICouponDiscount<T> {
BigDecimal discountAmount(T couponInfo,BigDecimal skuPrice);
}

定义了优惠券接口,也增加了泛型,不同类型的接口可以传递不同的参数类型。接口包括商品金额以及出参返回优惠后金额。在实际开发中,会比现在的接口参数多一些,但核心逻辑类似。

优惠券接口实现

满减
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.bestrook.design.event;
import com.bestrook.design.ICouponDiscount;
import java.math.BigDecimal;
import java.util.Map;
public class MJCouponDiscount implements ICouponDiscount<Map<String, String>> {
@Override
public BigDecimal discountAmount(Map<String, String> couponInfo, BigDecimal skuPrice) {
String x = couponInfo.get("x");
String o = couponInfo.get("o");
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;
}
}
直减
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.bestrook.design.event;
import com.bestrook.design.ICouponDiscount;
import java.math.BigDecimal;
public class ZJCouponDiscount implements ICouponDiscount<Double> {
@Override
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;
}
}
折扣
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.bestrook.design.event;
import com.bestrook.design.ICouponDiscount;
import java.math.BigDecimal;
public class ZKCouponDiscount implements ICouponDiscount<Double> {
@Override
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元购
1
2
3
4
5
6
7
8
9
package com.bestrook.design.event;
import com.bestrook.design.ICouponDiscount;
import java.math.BigDecimal;
public class NYGCouponDiscount implements ICouponDiscount<Double> {
@Override
public BigDecimal discountAmount(Double couponInfo, BigDecimal skuPrice) {
return new BigDecimal(couponInfo);
}
}
测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.bestrook.design;
import com.bestrook.design.event.MJCouponDiscount;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
public class TestApi {
public static void main(String[] args) {
//满减优惠
Context<Map<String, String>> mJContext = new Context<>(new MJCouponDiscount());
Map<String, String> mapReq = new HashMap<>();
mapReq.put("x","100");
mapReq.put("o","10");
System.out.println("减慢优惠:"+mJContext.discountAmount(mapReq,new BigDecimal(100)));

}
}

总结

这个演示的策略模式并不复杂,主要的逻辑体现在不同种类优惠券的计算折扣策略上。在实际的开发中,这种设计模式很常用。另外这种设计模式与命令模式、适配器模式的结构相似,但是思路略有些差异。通过使用策略模式,可以优化方法中的if语句。在使用这种设计模式后,可以很好的满足隔离性和扩展性要求,也方便承接不断新增的需求。

策略模式、适配器模式和组合模式等在某些方面是比较相似的,但是每一种设计模式有自己的逻辑特点,在使用的过程中需要经过多次实践来体会,积累经验。

评论