重学 Java 设计模式:实战策略模式「模拟多种营销类型优惠券,折扣金额计算策略场景」
设计足球战术板,模拟比赛策略 #生活乐趣# #运动乐趣# #足球乐趣#
作者:小傅哥
博客:https://bugstack.cn (opens new window)
沉淀、分享、成长,让自己和他人都能有所收获!
# 一、前言
文无第一,武无第二
不同方向但同样努力的人,都有自身的价值和亮点,也都是可以互相学习的。不要太过于用自己手里的矛去攻击别人的盾,哪怕一时争辩过了也多半可能是你被安放的角色不同。取别人之强补自己之弱,矛与盾的结合可能就是坦克。
能把复杂的知识讲的简单很重要
在学习的过程中我们看过很多资料、视频、文档等,因为现在资料视频都较多所以往往一个知识点会有多种多样的视频形式讲解。除了推广营销以外,确实有很多人的视频讲解非常优秀,例如李永乐老师的短视频课,可以在一个黑板上把那么复杂的知识,讲解的那么容易理解,那么透彻。而我们学习编程的人也是,不只是要学会把知识点讲明白,也要写明白。
提升自己的眼界交往更多同好
有时候圈子很重要,就像上学期间大家都会发现班里有这样一类学生不怎么听课,但是就是学习好。那假如让他回家呆着,不能在课堂里呢?类似的圈子还有:图书馆、网吧、车友群、技术群等等,都可以给你带来同类爱好的人所分享出来的技能或者大家一起烘托出的氛围帮你成长。
# 二、开发环境
JDK 1.8 Idea + Maven 涉及工程三个,可以通过关注公众号:bugstack虫洞栈 (opens new window),回复源码下载获取(打开获取的链接,找到序号18) 工程 描述 itstack-demo-design-20-01 使用一坨代码实现业务需求 itstack-demo-design-20-02 通过设计模式优化改造代码,产生对比性从而学习# 三、策略模式介绍
策略模式是一种行为模式,也是替代大量ifelse的利器。它所能帮你解决的是场景,一般是具有同类可替代的行为逻辑算法场景。比如:不同类型的交易方式(信用卡、支付宝、微信)、生成唯一ID策略(UUID、DB自增、DB+Redis、雪花算法、Leaf算法)等,都可以使用策略模式进行行为包装,供给外部使用。
策略模式也有点像三国演义中诸葛亮给刘关张的锦囊:
第一个锦囊:见乔国老,并把刘备娶亲的事情搞得东吴人尽皆知。 第二个锦囊:用谎言(曹操打荆州)骗泡在温柔乡里的刘备回去。 第三个锦囊:让孙夫人摆平东吴的追兵,她是孙权妹妹,东吴将领惧她三分。# 四、案例场景模拟
在本案例中我们模拟在购买商品时候使用的各种类型优惠券(满减、直减、折扣、n元购)
这个场景几乎也是大家的一个日常购物省钱渠道,购买商品的时候都希望找一些优惠券,让购买的商品更加实惠。而且到了大促的时候就会有更多的优惠券需要计算那些商品一起购买更加优惠!!!
这样的场景有时候用户用起来还是蛮爽的,但是最初这样功能的设定以及产品的不断迭代,对于程序员开发还是不太容易的。因为这里包括了很多的规则和优惠逻辑,所以我们模拟其中的一个计算优惠的方式,使用策略模式来实现。
# 五、用一坨坨代码实现
这里我们先使用最粗暴的方式来实现功能
对于优惠券的设计最初可能非常简单,就是一个金额的抵扣,也没有现在这么多种类型。所以如果没有这样场景的经验话,往往设计上也是非常简单的。但随着产品功能的不断迭代,如果程序最初设计的不具备很好的扩展性,那么往后就会越来越混乱。
# 1. 工程结构
itstack-demo-design-20-01 └── src └── main └── java └── org.itstack.demo.design └── CouponDiscountService.java
1
2
3
4
5
6
# 2. 代码实现
/** * 博客:https://bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获! * 公众号:bugstack虫洞栈 * Create by 小傅哥(fustack) @2020 * 优惠券折扣计算接口 * <p> * 优惠券类型; * 1. 直减券 * 2. 满减券 * 3. 折扣券 * 4. n元购 */ 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; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 六、策略模式重构代码
接下来使用策略模式来进行代码优化,也算是一次很小的重构。
与上面面向流程式的开发这里会使用设计模式,优惠代码结构,增强整体的扩展性。
# 1. 工程结构
itstack-demo-design-20-02 └── src └── main └── java └── org.itstack.demo.design ├── event │ └── MJCouponDiscount.java │ └── NYGCouponDiscount.java │ └── ZJCouponDiscount.java │ └── ZKCouponDiscount.java ├── Context.java └── ICouponDiscount.java
1
2
3
4
5
6
7
8
9
10
11
12
策略模式模型结构
# 2. 代码实现
# 2.1 优惠券接口public interface ICouponDiscount<T> { /** * 优惠券金额计算 * @param couponInfo 券折扣信息;直减、满减、折扣、N元购 * @param skuPrice sku金额 * @return 优惠后金额 */ BigDecimal discountAmount(T couponInfo, BigDecimal skuPrice); }
1
2
3
4
5
6
7
8
9
10
11
12
满减
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; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
直减
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; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
折扣
public class ZKCouponDiscount implements ICouponDiscount<Double> { /** * 折扣计算 * 1. 使用商品价格乘以折扣比例,为最后支付金额 * 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; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
N元购
public class NYGCouponDiscount implements ICouponDiscount<Double> { /** * n元购购买 * 1. 无论原价多少钱都固定金额购买 */ public BigDecimal discountAmount(Double couponInfo, BigDecimal skuPrice) { return new BigDecimal(couponInfo); } }
1
2
3
4
5
6
7
8
9
10
11
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); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
# 3. 测试验证
# 3.1 编写测试类(直减优惠)@Test public void test_zj() { // 直减;100-10,商品100元 Context<Double> context = new Context<Double>(new ZJCouponDiscount()); BigDecimal discountAmount = context.discountAmount(10D, new BigDecimal(100)); logger.info("测试结果:直减优惠后金额 {}", discountAmount); }
1
2
3
4
5
6
7
测试结果
15:43:22.035 [main] INFO org.itstack.demo.design.test.ApiTest - 测试结果:直减优惠后金额 90 Process finished with exit code 0
1
2
3
@Test public void test_mj() { // 满100减10,商品100元 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); }
1
2
3
4
5
6
7
8
9
10
测试结果
15:43:42.695 [main] INFO org.itstack.demo.design.test.ApiTest - 测试结果:满减优惠后金额 90 Process finished with exit code 0
1
2
3
@Test public void test_zk() { // 折扣9折,商品100元 Context<Double> context = new Context<Double>(new ZKCouponDiscount()); BigDecimal discountAmount = context.discountAmount(0.9D, new BigDecimal(100)); logger.info("测试结果:折扣9折后金额 {}", discountAmount); }
1
2
3
4
5
6
7
测试结果
15:44:05.602 [main] INFO org.itstack.demo.design.test.ApiTest - 测试结果:折扣9折后金额 90.00 Process finished with exit code 0
1
2
3
@Test public void test_nyg() { // n元购;100-10,商品100元 Context<Double> context = new Context<Double>(new NYGCouponDiscount()); BigDecimal discountAmount = context.discountAmount(90D, new BigDecimal(100)); logger.info("测试结果:n元购优惠后金额 {}", discountAmount);
1
2
3
4
5
6
7
测试结果
15:44:24.700 [main] INFO org.itstack.demo.design.test.ApiTest - 测试结果:n元购优惠后金额 90 Process finished with exit code 0
1
2
3
# 七、总结
以上的策略模式案例相对来说并不复杂,主要的逻辑都是体现在关于不同种类优惠券的计算折扣策略上。结构相对来说也比较简单,在实际的开发中这样的设计模式也是非常常用的。另外这样的设计与命令模式、适配器模式结构相似,但是思路是有差异的。 通过策略设计模式的使用可以把我们方法中的if语句优化掉,大量的if语句使用会让代码难以扩展,也不好维护,同时在后期遇到各种问题也很难维护。在使用这样的设计模式后可以很好的满足隔离性与和扩展性,对于不断新增的需求也非常方便承接。 策略模式、适配器模式、组合模式等,在一些结构上是比较相似的,但是每一个模式是有自己的逻辑特点,在使用的过程中最佳的方式是经过较多的实践来吸取经验,为后续的研发设计提供更好的技术输出。网址:重学 Java 设计模式:实战策略模式「模拟多种营销类型优惠券,折扣金额计算策略场景」 https://www.yuejiaxmz.com/news/view/175004
相关内容
【设计模式】策略模式 ( 简介优惠券管理策略:提高营销效果的关键
10 个优惠券营销策略技巧(销量猛增)
我在零售巨头做新零售(13):优惠券策略 – 人人都是产品经理,
五一优惠:实战经验引领下的促销策略新视角
【设计模式】代理模式
促销策略
乐事携手美团,开创休闲食品场景化营销新模式
23种设计模式总结及应用场景
二手书创业计划书——行业市场分析、营销策略分析、财务分析部分