1. 程式人生 > >《大話設計模式》之--第2章 商場促銷----策略模式

《大話設計模式》之--第2章 商場促銷----策略模式

2商場促銷----策略模式

2.1商場收銀軟體

“小菜,給你出個作業,做一個商場收銀軟體,營業員根據客戶所購買商品的單價和數量,向客戶收費。”

“就這個?木問題。”小菜說,“用兩個文字框來輸入單價和數量,一個確定按鍵來算出每種商品的費用,用個列表框來記錄商品的清單,一個標籤來記錄總計,對,還需要一個重置控制來重新開始,不就行了?!”

商場收銀系統v1.0關鍵程式碼如下:

“大鳥,”小菜叫道,“來看看,這不就是你要的收銀軟體嗎?我不到半個小時就搞定了啦。”

“哈哈,挺快的嘛。”大鳥說著,看了看小菜的程式碼。接著說:“現在我要求商場對商品搞活動,所有的商品打8折。”

“那不就是在totalPrices後面乘以個0.8嗎?”

“小子,難道商場活動結束,不打折了,你還要再改一遍程式碼,然後再用改後的程式把所有的機器全部安裝一次嗎?再說,還有可能因為週年慶,打五折的情況,怎麼辦?”

小菜不好意思道:“啊,我想的是簡單了點。其實呢,只要增加一個下拉選單選項框就可以解決你說的問題啦。”

大鳥笑而不語。

2.2增加打折

商場收銀系統v1.1關鍵程式碼如下:

“這下可以了吧,只要我事先把商場可能的打折都做成下拉選單的樣子,就可以了”小菜說道。

“這比剛才靈活性上是好了,不過重複程式碼很多,4個分支語句除了打折多少不同以外幾乎完全一樣,應該考慮重構一下。不過這還不是最主要的,現在我的需求又來了,商場的活動加大,需要有滿300100的促銷演算法,你說該怎麼辦?”

“滿300100,那要是700就要返200了?這個必須要寫成函數了吧?”

“小菜啊,看來之前教你的都白教了,這裡面看不出來什麼名堂嗎?”

“哦!我想起來了,你的意思是簡單工廠模式,對對對,我可以先寫一下父類,再繼承它實現多個打折和反利的子類,複用多型性來完成這個程式碼。”

“那你打算寫幾個子類?”

“根據需求嘛,比如

8折、7折、5折、滿300100、滿20050…要幾個寫幾個。”

“小菜又不動腦子了,有必要這樣寫嗎?如果我現在是3折,我要滿30080,你難道再去增加子類?你不想想看,這當中哪些是相同的,哪些是不同的?”

2.3簡單工廠實現

大鳥:“對的,這裡打折基本都是一樣的,只要有個初始化引數就可以了。滿幾送幾的,需要兩個引數才行,明白,現在看來不麻煩了。”

大鳥:“面向物件的程式設計,並不是類越多越好,類的劃分是為了封裝,但分類的基礎是抽象,具有相同的屬性和功能的物件的抽象集合才是類。打1折和打9折只是形式不同,抽象分析出來,所有的打折演算法都是一樣的,所以打折演算法應該是一個類。好了,空話說的太多了,寫出來才是硬道理。”

大約1個小時後,小菜交出了第三份作業。

程式碼結構圖

“搞定,這次無論你要怎麼改,我都可以簡單處理完成。”小菜自信滿滿地說。

“是嗎?我要的是需要打5折和滿500200的促銷活動,如何辦?”

“只要在現金工廠中加兩個條件,在在介面的下拉選單中增加兩項即可。”

“說的不錯,如果我現在需要增加一種商場促銷手段,滿100積分10點,以後積分到一定時候可以領取獎品如何做?”

“有了工廠,何難?加一個積分演算法,構造方法有兩個引數:條件和返點,讓它繼承CashSuper,再到現金工廠增加滿100積分10點的分支條件,再到介面稍加改動就行了。”

“嗯,不錯,你的簡單工廠模式運用的很熟練了嘛,簡單工廠模式雖然也能解決這個問題,但是這個模式只是解決物件的建立問題,而且由於工廠本身包括了所有的收費方式,商場是可能經常性地更改打折額度和返利額度的,每次維護或擴充套件收費方式都要改動這個工廠,以致程式碼需要重新編譯部署,這是非常糟糕的處理方式,所以用它不是最好的辦法。面對演算法的時常變動,應該有更好的辦法。好好去研究一下其他的設計模式,你會找到答案的。”

小菜進入沉思狀態

2.4策略模式

小菜次日找到大鳥:“我找到相關的設計模式了,應該是策略模式(Strategy)。策略模式定義了演算法家族,分別封裝起來,讓它們之間可以互相替換,此模式讓演算法的,不會影響到使用演算法的客戶。看來商場收銀系統應該考慮用策略模式?”

策略模式(Strategy:它定義了演算法家族,分別封裝起來,讓它們之間可以互相替換,此模式讓演算法的變化,不會影響到使用演算法的客戶。

“你問我?你說呢?”大鳥笑道:“商場收銀時,如何促銷,用打折還是返利,其實都是一些演算法,用工廠來生成演算法物件,這沒有錯,但演算法本身只是一種策略,最重要的是這些演算法是隨時都可能互相替換的,就這點變化,而封裝變化點是我們面向物件的一種很重要的思維方式。我們來看看策略模式的結構圖和基本程式碼。”

策略模式(Strategy)結構圖

2.5策略模式實現

小菜:“我明白了,我昨天寫的CashSuper就是抽象策略,而正常收費CashNormal、打折收費CashRebate和返利收費CashReturn就是三個具體策略,也就是策略模式中說的具體演算法,對吧?”

“是的哇,來吧,你模仿策略模式的基本程式碼,改動一下你的程式。”

“其實不麻煩的說,原來寫的CashSuperCashNormalCashRebateCashReturn都不用改了,只要加一個CashContext類,並改一下客戶端就可以了。”

商場收銀系統v1.2

程式碼結構圖

“大鳥,程式碼雖然是模仿出來了,但我感覺還是回到原來的老路子了,在客戶端判斷用哪一個演算法。”

“是的,但是你有沒有什麼好辦法,把這個判斷過程從客戶端程式中轉移走呢?”

“轉移?不明白,原來我用簡單工廠模式可以轉移,現在這樣子如何做?”

“難道簡單工廠就一定要是一個單獨的類嗎?難道不可以與策略模式的Context結合?

“我明白了,試試。”

2.6策略與簡單工廠結合

 

“嗯,原來簡單工廠模式並非只有建一個工廠類的做法,還可以這樣子做。此時比剛才的模仿策略模式的寫法要清楚多了,客戶端程式碼簡單明瞭。”

“那和你寫的簡單工廠客戶端程式碼相比,觀察一下,找出它們的不同之外。”

 

“你的意思是,簡單工廠模式我需要讓客戶端認識兩個類,CashSuperCashFactory,而策略模式與簡單工廠模式結合的用法,客戶端就只需要認識一個類CashContext。耦合度更加降低。”

“說的木有錯,我們在客戶端例項化的是CashContext的物件,呼叫的是CashContext的方法,這使得具體的收費演算法徹底地與客戶端分離。連演算法的父類CashSuper都不讓客戶端認識了。相當於建立了一個控制代碼類。”

2.7策略模式解析

大鳥:“回過頭來反思一下策略模式,策略模式是一種定義一系列演算法的方法,從概念上來看,所有這些演算法完成的都是相同的工作,只是實現不同,它可以以相同的方式呼叫所有的演算法,減少了各種演算法與使用演算法之間的耦合。”

小菜:“策略模式還有什麼優點?”

大鳥:“策略模式的Strategy類層次為Context定義了一系列的可供重用的演算法或行為。繼承有助於析取出這些演算法中的公共功能。對於打折、返利或者其他的演算法,其實都是對實際商品收費的一種計算方式,通過繼承,可以得到它們的公共功能,你說這公共功能指蝦米?”

小菜:“公共的功能就是獲得計算費用的結果getResult,這使得演算法間有了抽象的父類CashSuper。”

大鳥:“不錯,另外一個策略模式的優點是簡化了單元測試,因為每個演算法都有自己的類,可以通過自己的介面單獨測試。”

小菜:“每個演算法可保證它沒有錯誤,修改其中任一個時也不會影響其他的演算法,這真是的非常的好。”

大鳥:“哈,小菜今天表現的不錯,我所想的你都想到了。還有,在最開始程式設計時,不得不在客戶端的程式碼中為了判斷用哪一個演算法計算而用了if條件分支,這也是正常的。因為當不同的行為堆砌於一個類中,就很難避免使用條件語句來選擇合適的行為。將這些行為封裝在一個個獨立的Strategy類中,可以在使用這些行為的類中消除條件語句。就商場收銀系統的例子而言,在客戶端的程式碼中就消除條件語句,避免了大量的判斷。這是非常重要的進展。你能用一句話來概況這個優點嗎?”

小菜:“策略模式封裝了變化。”

大鳥:“說的非常好,策略模式就是用來封裝演算法的,但在初中中,我們發現可以用它來封裝幾乎任何型別的規則,只要在分析過程中聽到需要在不同時間應用不同的業務規則,就可以考慮使用策略模式處理這種變化的可能性。”

小菜:“但我感覺在基本的策略模式中,選擇所用具體實現的職責由客戶端物件承擔,並轉給策略模式的Context物件。這本身並沒有解除客戶端需要選擇判斷的壓力,而策略模式與簡單工廠模式結合後,選擇具體實現的職責也可以由Context來承擔,這就最大化地減輕了客戶端的職責。”

大鳥:“是的,這已經比起初的策略模式好勝了,不過,它依然不夠完美。”

小菜:“還有什麼不足嗎?”

大鳥:“因為在CashContext裡還是用到了ifswitch,也就是說,如果我們需要增加一種演算法,比如滿20050,你就必須要改CashContext中的ifswitch程式碼,這讓人感覺很不happy的說!”

小菜:“啊,那你說怎麼辦,有需求就得改啊,任何需求的變更都是需要成本的。”

大鳥:“是的哇,但是成本的高低還是有差異的嘛。高手和菜鳥的區別就是高手可以花同樣的代價獲得最大的收益或者說做同樣的事花最小的代價。面對同樣的需求,當然是改動越小越好啦。”

小菜:“你的意思是說,還有更好的辦法?”

大鳥:“當然啦,要不然我怎麼會這樣說哩,這個辦法就是用到了反射技術,不是常有人講:‘反射反射,程式設計師的快樂’,不過今天就不講了,以後會再提它的。”

“反射真的有這麼神奇?”小菜疑惑地望向了遠方。

(注:在抽象工廠模式章節有對反射的講解)