iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 3
0
自我挑戰組

來讀設計模式:Junior developer 跟大家一起練功系列 第 10

DAY10: Strategy 模式1

今天要討論的是 Strategy 模式。這個模式會分做兩篇。

在今天這一篇,我首先先提出一個案例。遇到需求時,我們有什麼可能的解決方式?最後,我們在引入策略模式先概念性地去解決這個問題。

下一篇我們再實作出策略模式的簡單的程式碼,最後再深入去談策略模式的特徵。

案例研究

我們今天有一個台灣某個電子商務公司的訂單處理系統,這個系統必須要能處理不同國家的訂單。

這個系統的總架構是:Client 丟出處理銷售的請求時,會先進到一個 TaskController 物件裡做處理。它協助確認當請求出現時,它會將請求往下轉發給 SalesOrder 物件進行訂單處理。如下圖。

SalesOrder 物件的功能包括:

  • 允許客戶透過 GUI 填寫訂單
  • 處理(台灣)稅額的計算
  • 處理訂單,列印銷售收據

有些功能可能需要其他物件實作。例如,SalesOrder 物件沒有必要自己列印,它可以呼叫 SalesTicket 物件來列印。

新的需求產生

架設我們有了新的需求:修改處理稅額的方法。例如,我們現在必須處理台灣之外的顧客的訂單稅額。

幾種可能的做法

作者提到了幾種做法:

  • Copy-paste
  • Switch/if 語句
  • 繼承
  • 把整個功能委任給其他物件

我們一一分析這些做法。

copy-paste

複製貼上是最要不得的辦法,這會使專案維護者需要維護多份相似的程式碼。

switch/if 語句

這個方法一開始看起來合理,但是當 case 變多的時候,會讓整個程式碼過於冗長且不從容;而且假設今天需要加入加拿大的稅率計算,但是我們知道加拿大有英語區與法語區,兩區的稅率假設計算方式需要不同,那麼原本的 switch-case 裡又要再多加上一個 if 判斷。

多條直線分支又在四散,作者稱之為「分支蔓延 (switch creep) 」。

繼承

當繼承是使用 DAY8 的「類別再利用」方法。舉例來說,對於美國銷售訂單,我們可以從 SalesOrder 衍生出一個 AmericaSalesOrder,在裡頭再覆寫出自己的稅額處理規則,如圖所示。

但是這樣的解決方式會使我們建構的繼承層次無法靈活因應其他可能的變化。

同時,當衍生類別(愈來愈多國家的稅額處理)愈來愈多,容易產生太深的繼承層次,最終會讓程式碼難以理解,從而產生弱內聚、存在冗餘,和不容易測試。而這都是我們不想要的。

設計模式引入

我們必須考量「程式設計中什麼應該是可變的」、「對變化的概念進行封裝」,而且該「優先使用物件聚合,而不是類別繼承」。因此,我們會如此做:

  • 尋找變化,並將其封裝到一個類別中
  • 將這個類別包含在另一個類別中

根據這個策略,我們將繳稅規則 (CalTax) 封裝起來:

接下來,再用聚合取代繼承:

這就是 strategy 模式的一個實踐。這個圖有沒有很熟悉呢?沒錯,在 DAY8 的例子有出現過唷!

更詳細的內容我們明天再繼續!


上一篇
DAY9: 共通性與可變性分析;敏捷程式設計
下一篇
Day11: Strategy 模式2
系列文
來讀設計模式:Junior developer 跟大家一起練功22

尚未有邦友留言

立即登入留言