韶光:2月27日22点  地点:大鸟房间  人物:小菜、大鸟

"小菜,给你出个作业,做一个阛阓收银软件,业务员根据客户所购买商品的单价和数量,向客户收费。
"

"就这个?没问题呀。
"小菜说,"用两个文本框来输入单价和数量,一个'确定'按钮来算出每种商品的用度,用个列表框来记录商品的清单,一个标签来记录总计,不就行了?!
"

超市促销php源码跪拜年夜佬畅谈商场促销策略模式受益匪浅 Docker

阛阓收银系统v1.0关键代码如下。

"大鸟,"小菜叫道,"来看看,这不便是你要的收银软件吗?我不到半小时就搞定了。
"

"哈哈,很快嘛,"大鸟说着,看了看小菜的代码。
接着说:"现在我哀求阛阓对商品搞活动,所有的商品打八折。
"

"那不便是在totalPrices后面乘以一个0.8吗?"

"小子,难道阛阓活动结束,不打折了,你还要再改一遍程序代码,然后再用改后的程序去把所有机器全部安装一次吗?再说,还有可能由于周年庆,打五折的情形,你怎么办?"

小菜不好意思道:"啊,我想得是大略了点。
实在只要加一个下拉选择框就可以办理你说的问题。
"

大鸟微笑不语。

2 增加打折

阛阓收银系统v1.1关键代码如下。

"这下可以了吧,只要我事先把阛阓可能的打折都做成下拉选择框的项,要变革的可能性就小多了。
"小菜说道。

"这比刚才灵巧性上是好多了,不过重复代码很多,比如3个分支要实行的语句除了打折多少以外险些没什么不同,该当考虑重构一下。
不过这还不是最紧张的,现在我的需求又来了,阛阓的活动加大,须要有满300返100的匆匆销算法,你说怎么办?"

"满300返100,那假如700就要返200了?这个必须要写函数了吧?"

"小菜呀,看来之前教你的白教了,这里面看不出什么名堂吗?"

"哦!
我想起来了,你的意思是大略工厂模式是吧,对的对的,我可以先写一个父类,再继续它实现多个打折和返利的子类,利用多态,完成这个代码。
"

"你打算写几个子类?"

"根据需求呀,比如八折、七折、五折、满300送100、满200送50……要几个写几个。
"

"小菜又不动脑筋了,有必要这样吗?如果我现在要三折,我要满300送80,你难道再去加子类?你不想想看,这当中哪些是相同的,哪些是不同的?"

3 大略工厂实现

"对的,这里打折基本都是一样的,只要有个初始化参数就可以了。
满几送几的,须要两个参数才行,明白,现在看来不麻烦了。
"

"面向工具的编程,并不是类越多越好,类的划分是为了封装,但分类的根本是抽象,具有相同属性和功能的工具的抽象凑集才是类。
打一折和打九

折只是形式的不同,抽象剖析出来,所有的打折算法都是一样的,以是打折算法该当是一个类。
好了,空话已说了太多,写出来才是真的懂。
"

大约1个小时后,小菜交出了第三份作业。

客户端程序紧张部分:

"大鸟,搞定,这次无论你要怎么改,我都可以大略处理就行了。
"小菜自傲满满地说。

"是吗,我假如须要打五折和满500送200的匆匆销活动,如何办?"

"只要在现金工厂当中加两个条件,就OK了。
"

"现金工厂?!
你当是生产钞票呀。
是收费工具天生工厂才准确。
说得不错,如果我现在须要增加一种阛阓匆匆销手段,满100积分10点,往后积分到一定时候可以领取奖品如何做?"

"有了工厂,何难?加一个积分算法,布局方法有两个参数:条件和返点,让它继续CashSuper,再到现金工厂,哦,不对,是收费工具天生工厂里增加满100积分10点的分支条件,再到界面稍加改动,就行了。
"

"嗯,不错。
你对大略工厂用得很闇练了嘛。
"大鸟接着说:"大略工厂模式虽然也能办理这个问题,但这个模式只是办理工具的创建问题,而且由于工厂本身包括所有的收费办法,阛阓是可能常常性地变动打折额度和返利额度,每次掩护或扩展收费办法都要改动这个工厂,甚至代码需重新编译支配,这真的是很糟糕的处理办法,以是用它不是最好的办法。
面对算法的时常变动,该当有更好的办法。
好好去研究一下其他的设计模式,你会找到答案的。
"

小菜进入了沉思中……

4 策略模式

韶光:2月28日19点  地点:大鸟房间  人物:小菜、大鸟

小菜越日来找大鸟,说:"我找到干系的设计模式了,该当是策略模式(Strategy)。
策略模式定义了算法家族,分别封装起来,让它们之间可以相互更换,此模式让算法的变革不会影响到利用算法的客户。
看来阛阓收银系统该当考虑用策略模式?"

"你问我?你说呢?"大鸟笑道,"阛阓收银时如何匆匆销,用打折还是返利,实在都是一些算法,用工厂来天生算法工具,这没有错,但算法本身只是一种策略,最主要的是这些算法是随时都可能相互更换的,这便是变革点,而封装变革点是我们面向工具的一种很主要的思维办法。
我们来看看策略模式的构造图和基本代码。
"

策略模式(Strategy)构造图

Strategy类,定义所有支持的算法的公共接口:

ConcreteStrategy类,封装了详细的算法或行为,继续于Strategy:

Context类,用一个ConcreteStrategy来配置,掩护一个对Strategy工具的引用。

客户端代码:

5 策略模式实现

"我明白了,"小菜说,"我昨天写的CashSuper便是抽象策略,而正常收费CashNormal、打折收费CashRebate和返利收费CashReturn便是三个详细策略,也便是策略模式中说的详细算法,对吧?""是的,来吧,你模拟策略模式的基本代码,改写一下你的程序。
"

" 其 实 不 麻 烦 , 原 来 写 的 CashSuper 、 CashNormal 、 CashRebate 和CashReturn都不用变动了,只要加一个CashContext类,并改写一下客户端就行了。
"

阛阓收银系统v1.2:

CashContext类:

客户端紧张代码:

"大鸟,代码是模拟着写出来了。
但我觉得这样子做不又回到了原来的老路了吗,在客户端去判断用哪一个算法?"

"是的,但是你有没有什么好办法,把这个判断的过程从客户端程序转移走呢?"

"转移?不明白,原来我用大略工厂是可以转移的,现在这样子如何做到?"

"难道大略工厂就一定假如一个单独的类吗?难道不可以与策略模式的Context结合?"

"哦,我明白你的意思了。
我试试看。
"

6 策略与大略工厂结合

改造后的CashContext:

客户端代码:

"嗯,原来大略工厂模式并非只有建一个工厂类的做法,还可以这样子做。
此时比刚才的模拟策略模式的写法要清楚多了,客户端代码大略明了。
"

"那和你写的大略工厂的客户端代码比呢?不雅观察一下,找出它们的不同之处。
"

"你的意思是说,大略工厂模式我须要让客户端认识两个类,CashSuper和CashFactory,而策略模式与大略工厂结合的用法,客户端就只须要认识一个类CashContext就可以了。
耦合更加降落。
"

"说得没错,我们在客户端实例化的是CashContext的工具,调用的是CashContext的方法GetResult,这使得详细的收费算法彻底地与客户端分离。
连算法的父类CashSuper都不让客户端认识了。
"

7 策略模式解析

"回过分来反思一下策略模式,策略模式是一种定义一系列算法的方法,从观点上来看,所有这些算法完成的都是相同的事情,只是实现不同,它可以以相同的办法调用所有的算法,减少了各种算法类与利用算法类之间的耦合[DPE]。
"大鸟总结道。

"策略模式还有些什么优点?"小菜问道。

"策略模式的Strategy类层次为Context定义了一系列的可供重用的算法或行为。
继续有助于析取出这些算法中的公共功能[DP]。
对付打折、返利或者其他的算法,实在都是对实际商品收费的一种打算办法,通过继续,可以得到它们的公共功能,你说这公共功能指什么?"

"公共的功能便是得到打算用度的结果GetResult,这使得算法间有了抽象的父类CashSuper。
"

"对,很好。
其余一个策略模式的优点是简化了单元测试,由于每个算法都有自己的类,可以通过自己的接口单独测试[DPE]。
"

"每个算法可担保它没有缺点,修正个中任一个时也不会影响其他的算法。
这真的是非常好。
"

"哈,小菜本日表现不错,我所想的你都想到了。
"大鸟表扬了小菜,"还有,在最开始编程时,你不得不在客户真个代码中为了判断用哪一个算法打算而用了switch条件分支,这也是正常的。

由于,当不同的行为堆砌在一个类中时,就很难避免利用条件语句来选择得当的行为。
将这些行为封装在一个个独立的Strategy类中,可以在利用这些行为的类中肃清条件语句[DP]。
就阛阓收银系统的例子而言,在客户真个代码中就肃清条件语句,避免了大量的判断。
这是非常主要的进展。
你能用一句话来概况这个优点吗?"大鸟总结后问道。

"策略模式封装了变革。
"小菜快速而武断地说。

"说得非常好,策略模式便是用来封装算法的,但在实践中,我们创造可以用它来封装险些任何类型的规则,只要在剖析过程入耳到须要在不同韶光运用不同的业务规则,就可以考虑利用策略模式处理这种变革的可能性[DPE]"。

"但我觉得在基本的策略模式中,选择所用具体实现的职责由客户端工具

承担,并转给策略模式的Context工具[DPE]。
这本身并没有解除客户端须要选择判断的压力,而策略模式与大略工厂模式结合后,选择详细实现的职责也可以由Context来承担,这就最大化地减轻了客户真个职责。
"

"是的,这已经比起初的策略模式好用了,不过,它依然不足完美。
"

"哦,还有什么不敷吗?"

"由于在CashContext里还是用到了switch,也便是说,如果我们须要增加一种算法,比如'满200送50',你就必须要变动CashContext中的switch代码,这总还是让人很不爽呀。
"

"那你说怎么办,有需求就得改呀,任何需求的变更都是须要本钱的。
"

"但是本钱的高低还是有差异的。
高手和菜鸟的差异便是高手可以花同样的代价得到最大的收益或者说做同样的事花最小的代价。
面对同样的需求,当然是改动越小越好。
"

"你的意思是说,还有更好的办法?"

"当然。
这个办法便是用到了反射(Reflect)技能,不过本日就不讲了,往后会再提它的。
"

"反射真有这么神奇?"小菜迷惑地望向了远方。

注:在抽象工厂模式章节有对反射的讲解。