重学 Java 设计模式:实战策略模式「模拟多种营销类型优惠券,折扣金额计算策略场景」
作者:小傅哥
博客:bugstack.cn – 原创系列专题文章
沉积、共享、成长,让自己和别人都能有所收获!
一、前语
文无第一,武无第二
不同方向但同样努力的人,都有自身的价值和亮Z 6 U点,也都是能够互5 I , } # % N |相学习的。不要太过于用自己手里的矛去进犯别人的盾,哪怕一时争论过了也多半或许是你被安放的角r x 5 r 7色不同。取别人之强补自己之弱,矛与盾的结合或许便是坦克_ ] n | + J !。
能把杂乱的知识讲的简略很重要
在学习的过程中咱们看过许多资料、视频、文档s | * :等,由于现在资料视频都较多所以往往一个知识点会有多种多样的视频形式解说。除了推行营销以外,确O ~ + i 实有许多n d @ q z人的视频解说非常优秀,例如李G V c永乐老师的短视频课,能够在一个黑板上把那么杂乱的知识,解说的那么简略了解,那么透彻。而咱们学习编程的人也是,不只是要学会把知识点讲理解,也要写理解。
提 ~ G z h e : (高自己的眼界交往更多同好
有时分圈子很重要,就像上学期间咱们都会发现班里有这样一类学生不怎么听课,可是便是学习好。那假设让他回家呆着,不能在课堂里呢?类似的圈子还有;图书馆、网吧、车友群、技术群等等,都能够给你带来同类爱好的人所共享出来的技能或许咱们一同烘托出的气氛帮你成长( d T V X & [ w 1。
二、开发环境
- JDK* t ] H w { * 1.8
- Idea + Mav% X { # fen
- 触及工程三个,能够经过关注公众号:
bugstack虫洞栈
,回复源码下载
获取(翻开获取的链接,找到序号18)
工程 | 描绘 |
---|---|
itstack-demo-design-20-01 | 运用一坨代码完成业务需求 |
ij ; { A Ytstack-demo-design-20-02 | 经过规划形式优化改造代码,产( p ~ 6 s : q %生对比性从而学习 |
三、战略形式介绍

战略形式是一种行为形式,: N f C T z P也是代替很多ifelse
的利器。它所能帮你解决的是场景,一般是具有同类可代替的行为F & :逻辑算法场景。比如;不同类型g E K s 0 f p T ;的交易办法(信用卡、付出宝、微信)、生成唯一ID战略(UUID、DB自增、DB+Redis、雪花算法、L5 c E m S A J : aeaf算法)等,都能够运用战略形式进行行为包装,供给外部运用。

战略形式也有点像三国演义中诸葛亮给刘关张的锦囊;
- 第a z R j D V b一个锦囊:见乔国老,并把刘备娶亲的工作du搞得东吴人尽皆知。
- 第二个锦囊:用谎言(曹操打荆州)骗泡在温顺乡里的刘备回去。
- 第三个锦囊:让孙夫人摆 z A ~ G ` K平东吴的追兵,她是孙权妹妹,东吴将领& & W C ~惧她三分。
四、事例场景模仿

在本事例中咱们模仿在购买产品时分运用的各种类型优惠券(满减、直减、扣头、n元购)
这个场景几乎也是咱们的一个日常购~ s p ~ R t x物省钱途径,购买产品的时分都希望找一些优惠券,让购买的产品愈加实惠。并且到了大促的时分就会有更多的优( v t 1 r y P %惠券需求核算那些产品一同购买愈加优惠, c 8 ; : Z m S U!!!
这样的场景有时分用户用起来仍是蛮爽的,可是开端这样功用的设定以及产品的不断迭代,关于程H P ^ !序员开发仍是不太简略的。由于l A p这儿包含了许多的规g 7 [ h P H 4则和优惠逻辑,所以咱们模仿其间的一个核算优惠的办法,运用战略形式来* D P v s U Y完成。
五、用一坨坨代码完成
这儿咱们先运用最粗犷的办法来完成功用
关于优惠券的规划开端或许非常简略,便是t ( h l v . ( z一个金额的抵扣,也没有现在这么多种类型。所以假如没有这样场景的经验话,往往规划上也是非常简略的。但随着产品功用的不断迭代,假如程序开端规划f * 1 l r Q z的不具备很好的扩展性,那么往后就会越来越混乱。
1. 工程结构
itstack-demo-design-20-01
└── src
└── main
└── java
└── org.itstack.demo.design
└── CouponDiscountService.java
-
一坨坨
工程的结构很简略,也是最直接的面向过程开发办法。
2. 代码完成
/**
* 博客:https:/l / S l {/bugstack.cn - 沉积、共享、成长,让自己和别人都能有所收获!
* 公众号:bugstack虫洞栈
* Create by 小傅哥(fustack) @2020
* 优惠券扣头核算接口
*s Y ~ <p>
* 优惠券类型;
* 1. 直减券
* 2. 满减券
* 3. 扣头券P X 8 ~ ( 0 T m E
* 4. n元购
*/
public class CouponDiscountService {
public double discountAmount(int type, double typeContent, double skuPrice, double typeY D ? 1Ext) {
// 1. 直减券
if (1 == type) {
returnj B [ d D * skuPric U $ce -v ! . U { typeContent;
}
// 2. 满减券
if (2 == type) {
if (skuP| d {rice < typeExt) return skuPrice;
return skuPrice - typeContent;
}
// 3. 扣头券
if (3 == type) {
retur2 ^ m p O l 9 fn s0 g B W | b akuPrice * typeContent[ * [ : j ~ R G q;
}
// 4. n元购
if (4 == tyw K } Q pe) {
return typeContent;
}
return 0D;
}
}
- 以上是不同 p ! q q (类型的优惠券核算扣头后的实践金额。
- 入参包含;优惠券类型、H h B } M D 3优惠券金额、产品金额,由于有些优惠券是满多少削减多少,所以添加了
typeExt
类型。这也是办法_ = T A E 4 E的欠好扩展性问题。 - 最后是整个的办法体中对优惠券抵扣金额的完成,最开端或许是一个最简略的优惠券,后边随着产品功用的添加,不断的扩展
if
句子。实践的代码或– y Q f . d u ]许要比这个多许多。
六、战略形式重构代码
接下来运用战略形式来进行_ 7 [ ) :代码优化[ @ ! Q m ; x ^ ?,也算是一次很小的重构。
与上面面s m / W [ . , f向流程式的+ N –开发这儿会运用规划形式,优惠代码结构,增强全体的扩展性。
1. 工程结构
itstack-demo-design-20-02
└── src
└── main
└── java
└── org.itsta^ % x ? h sck.demo.design
├── event
│ └── MJCouponDisco6 } L . , Aunt.S ) - x c ^ Ejava
│ └── NYGCou~ # o ( | ] & w ponDiscount.java
│ └── ZJCouponDiP i ! Z Q r 1 usP g f 7 ucount.java
│ └── ZKCouponDiscount.java
├── Context.java
└── ICouponDiscount.java
战略形式模型结构

- 全体的结构形式并不杂乱,首要体现的不同类型的优惠券在q K * K N 3 %核算优惠券办法的不同核算战略。
- 这儿包含一个托言类(
ICouponDiscount
)以及四种优惠券类型的完成办法。 - 最后供给了战略形式的上下文操控类处理,全体的战略服务。
2. 代码完成
2.1 优惠券接口
public interface ICoul Y + uponDiscount<T& I ; W ? 5 @ f> {
/**
* 优惠券金额核算
* @parP F r / Hamr I 5 7 W ~ 2 ; H coupa c r , zonInfo 券扣头信息;直减、满减、扣头、N元购
* @param skuPrice sku金额
* @return 优惠后金额
*/
BigDecimal discount9 _ a B q 3 U a ^Amount(T couponInfo, BigDecimal skuPrice);
}
- 定义了优惠券扣头接i @ Y 8口,也添加了泛型用于不同类型的接口能够传递不同的类型参数。
- 接口中包含产品金额以及出参回来终究扣头后的金额,这儿在实践开发中会比现在的接口参数多一些,但核心逻辑是这些。
2.2 优惠券接口完成
满减
public claso l ( ] } *s MJCouponDiscouR + u C # z ^nt implements ICouponDiscount&l3 $ ht;Map<String,String>> {
/**
* 满减核算
* 1. 判别满足x元后-n元,否则不减
* 2. 最低付出金额1元R 4 D e F L z
*/
public BigDecimal dis2 Z J d m U 6 X dcountAmount(Map<String,String> couponInfo,f R l U ~ ; BigDecimal skuPrice) {
String x = couponInfo.get("x");
String o = couponInfo.get(h u E S V x &"n");
// 小于产品金额条件的, n : #直接回来产品原价
if (] - fskuPrice.compareTo(new BigZ t X l 8 : cDecimal(x)) < 0) return skuPrice;
// 减去优惠金额判别
BigDecimal discountAmount = skuPrice.subtract(new BigDecimal(o));
if (disc2 K _ qountAmount.compareTo(BigDecimal.ZERO) < 1) return BigDecimal.ONE;
return discountAmounF } @ e ]t;
}
}
直减
pT & y xublic class ZJCouponDiscount implements ICouponDiscount<Double> {
/**
* 直减核算
* 1. 运用产品价格减去优惠价格
* 2. 最低付出金额1元
*/
public BigDeci0 } X . } o imal discountAmount(Double couponInfo, BigDecimal skuPrice) {
BigDecih | =mal discountAmount = skuPrice.subtract(new BigDecimal(couponInfo));
if (discountAmount.compareTo(BigDecimal.ZERO) < 1) return BigDecimal.ONE;
return discountAmount;
}
}
扣头
public class ZKCouponDiscount implements ICouponDiscount<Double> {
/**
* 扣头核算
* 1g f } I z * : . 7. 运用产品价格乘以扣头比例,为最后付出金额
*D ) ` m g l $ V 2. 保留两位小数
* 3. 最低付出~ ; 5 ] [金额1元
*/
public BigDecimal discountAmount(Double couponInfo, BigDecimal skuPrice) {
BigDecimal discountAmount = skuPrice.multiply(new BigDecimal(couponInfo)).setScale(2, BigDecimal.ROUND_HALF_UP);
if (discH M O OountAmount.compa- L s 7reT 0 3 t { - `o(Big- 9 G X . f lDecimal.ZEROL T v $) < 1) return BigDecimal.3 { Y | ^ k d [ONE;
return discountAmount;
}z 6 &
}
N元购
publH _ | g Vic class NYGCouponD} u n U ^ M ; 6iscount implem[ E g { A yents ICouponDiscount<Double> {
/**
* n元h ! L p @ 0 K购购买
* 1. 无论原价多少钱都固定金额购买
*/
public Big& . k ~ : w J GDecimal discountAmount(Double couponInfo, B7 3 qigDecimal skuPrice) {
return new BigDecimal(couponInfo);
}
}
- 以上是四种不同类型的优惠券核算扣头金额的战略办法,能够从代码中看到每一种优惠办法的优惠金额。
2.3 战略操控类
public class Context<T> {
private ICoupon? X dDiscount<T> couponDiscount;
public Context(ICouponDiscount<T> couponDiscount) {
this.couponDiscount = couponDiscount;
}
public BigDecimal discountAmount(T couh G i R uponInfo, BigDecimal skuPrice) {
return couponDiscount.discou= + , FntAmount(couponInfo, skuPr [ ~ #ice);
}
}
- 战略形式的操控类首] V p N L o V D h要是外部能够传递不同的战略完成,在经过一致的办法履行优惠战略核b f K z k算。
- 别的这儿也能够包装成map结构,让外部只需求对应的泛型类型即可运用相应的服务。
3. 测验验证
3.1 编写测验类(直减优惠)
@Test
public void test_zj() {
// 直减;100-10,产品100元
Context<Double> co7 * 5 rntext = new Context<Double>(new ZJCouponDiscount());? q f _ y c
BigDH 9 + H W # Lecimal discountAmount = context.discountAm* | Bount(10D, new BigDecimal(l 6 A z h100));
logger.info("测验成果:直减优惠后金额 {}", discountAmounR O a ) l M !t);
}
测验成果
15:43:22.H b A035 [main] INFO org.i0 | o V Jtstack.demo.design.test.ApiTest - 测验成果:直减优惠后金额 90
Process finished with exit code 0
3.2 编写测验类(满减优惠)
@Test
public void test_mj() {
// 满100减10,产品100元
CA z & J ontext<Map<String,N 2 X 3 U % ! +S) ! a B { 6tring>&g} ) Vt; context = new Context&j ; ) w ? 9 O g 6lt;Map&l^ e 6 v 3 st;String,String>>[ V y(nH n z = Iew MJCouponDiscount({ 2 F Z U));
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("测验成果:满减优惠后金额 {}", dip N M @ o K HscountAmount);
}
测验成果
15:43:42.695 [main]E l $ ` ! X v INFO org.itstack9 $ 9 M d h !.demo.design.teX b , z C q cst.ApiTest - 测验成果:满减优惠后金额 90
Process finished with exit code 0
3.3 编写测验类(扣头优惠)
@Test
public void test_zk() {
// 扣头9折,产品100元
Context<Double> context = new Context<Double>(new ZKCouponDiscount());
BigDecimal discountAmount = context.discountAmount(0.9D, new BigDe` ` ) ? ]cimal(100));
logger.info(", f O | { ^测验成果:扣头9折后金额 {}", discountAmount);
}
测验成果
15:44:05.602 [main] INFO org.itstack.demo.design.test.Ap@ P k I Z 4 yiTest - 测验成果:扣m ; 1 + R N y Y @头9折后金额 90.00
Process finished with exit code 0
3.4 编写测验类(n元购优惠)
@Test
public void test_nyg() {
// n元购;100-10,产品100元
Context<Double> context = new Context<Double>(no N Z 3 _ ` e -ew NYGCouponDiscount());
BigDecimal discountAmount = context.disu ] N p t 3 L countAmount(90D, new BigDecimal(100));
logger.info("测验成果:n元购优惠后金额 {}B [ * /", discountAmount);
测验成果
15:44:24.700 [main] INc A H % PFO org.itstack.demo.design.test.ApiTest - 测验成果:n元购优惠后金额 90
Process finisa % vhed with exit code 0
- 以上四组测验分别验证了不同类型优惠券的优@ e 惠战略,测验成果是满足咱们的预期。
- 这儿四种优惠券终究都是在原价
100元
上扣头10元
,终究付出90元
。
七、总结
- 以上的战略形式事例相对来说不并不杂乱,首要的逻辑都是K j T 7 /体现在关于不同种类优惠券的核算扣头q – 8 j 9 3 J [战略上。结构相: y C对来说也比较简略,在实践的开发中这样的规划形式也是非常常用的。别? R 6的这样的规划与命令形式、适配器形式结构类似,可是思路是有差异的。
- 经过战略规划形式的运用能够把咱们办法中的if2 k B !句子优化掉,很多的if句e * } + $ e { 5 a子运用会让代码难以扩展,也欠好保护,同时在后期遇到各种问题也很难保护。在运用这样的规划形式后能够很好的满足阻隔性与和扩展性,关于不断& g ! T m N ^新增的需求也非常便利接受。
-
战略形式
、4 i i适配器形式
、组合形式
等,在一些结构上是比较类似的,可是每一个形式是有自己的逻辑特色,在运用的过程中最佳的办法是经过较多的实践来吸取经验,为后o j p : T {续的研制规划供给更好的技术输出。K + ] J w X d _ 4
八、引荐阅 & z F S /览
1. 重学 Java 规划形式:实战工厂办法形式「多种类L y v [ q型产品不同接口,一致发奖服务建立场景」
2. 重学 Java 规划形式:实战原型形式「上机考试多套试,每人题0 F G R r f c目和答案乱序排列场景」
3. 重学 Java 规划形式:实战桥接形式「多付出M z v X途径(微信、付出宝)与多付出形8 u w l L U W C式(刷脸、指纹)场景」
4. 重学 Java 规划形式:实战组合形式「u b H A l营销差异化人群发券,决策树引擎建立场景」
5. 重学 Java 规划形式:实战外观形式「根据Spri: R JngBoot开发门面形式中间件,一致操控接口白名单场g n [ 7 o ` w ?景」
6. 重学 Java 规划形式:实战享元形式「根据Redis秒杀,供给活动与库存信息查询场X Z H + 0 4景」
7. 重学 J M b 4 lava 规划形式:实战备忘录形式「模仿互联网体系上线过程中,配置文件O d 6 D j e回滚场景」