學習完了簡單工廠模式以後,感覺可以用於解決大多數多演算法選擇的問題了,接下來看一個新的例子,也是借用《大話數據結構》一書的例子,要實現的是一個商場促銷的計算軟體,輸入商品的單價和數量,還有銷售策略(如打折,返現),然後計算出總的價格。 首先可以從這個問題中抽象出兩個演算法類,一個是打折演算法類,需要提供打 ...
學習完了簡單工廠模式以後,感覺可以用於解決大多數多演算法選擇的問題了,接下來看一個新的例子,也是借用《大話數據結構》一書的例子,要實現的是一個商場促銷的計算軟體,輸入商品的單價和數量,還有銷售策略(如打折,返現),然後計算出總的價格。
首先可以從這個問題中抽象出兩個演算法類,一個是打折演算法類,需要提供打折的幅度,如八折,另一個是返現演算法類,需要提供返現的最小額度和返現金額,如滿300減100,它們都有一個共同的方法,計算出總的價格,這裡因為每個類裡面只有一個計算總價的方法需要用到金額,所以可以直接把總金額作為函數的參數傳入,而不用作為演算法類的成員變數,用簡單工廠模式實現如下:
1 class Collection 2 { 3 public: 4 virtual float GetSum(float money) { return money; } 5 virtual ~Collection() {} 6 }; 7 class CollectionDiscount :public Collection 8 { 9 private: 10 float discount = 1.0f; 11 public: 12 CollectionDiscount() = default; 13 CollectionDiscount(float d):discount(d){} 14 float GetSum(float money) override 15 { 16 return money*discount; 17 } 18 }; 19 class CollectionReturn :public Collection 20 { 21 private: 22 float limit=0.0f; 23 float value = 0.0f; 24 public: 25 CollectionReturn() = default; 26 CollectionReturn(float l,float v) :limit(l),value(v) {} 27 float GetSum(float money) override 28 { 29 if (money >= limit) 30 return money - value; 31 else 32 return money; 33 } 34 }; 35 class CollectionFactory 36 { 37 public: 38 static shared_ptr<Collection> CreateCollection(string type) 39 { 40 shared_ptr<Collection> ptr = nullptr; 41 if (type == "打八折") 42 ptr = make_shared<CollectionDiscount>(0.8); 43 else if (type == "滿300送100") 44 ptr= make_shared<CollectionReturn>(300, 100); 45 else 46 throw(invalid_argument("沒有這種折扣策略")); 47 return ptr; 48 } 49 };
1 int main() 2 { 3 shared_ptr<Collection> c = CollectionFactory::CreateCollection("打八折"); 4 cout << c->GetSum(100) << endl; 5 return 0; 6 }
這樣工廠類的對象就可以通過輸入的字元串來判斷,並生成對應的演算法類的對象,最後客戶端就能調用演算法類中的方法進行計算了,簡單工程模式很好的解決的了對象的創建問題,但是有一個問題就是演算法類的名稱還是出現在了客戶端程式中,那麼對於大型商場而言,它的銷售策略千奇百怪,總會出現很多新的銷售策略演算法,那麼有沒有一種模式,能完全不讓演算法類的名字出現在客戶端程式。面對這種演算法時常變動,可以採用策略模式。
策略模式就是定義了演算法家族,將它們分別封裝起來,讓它們之間可以相互替換,可以讓演算法變化,但是不會影響到使用演算法的客戶。在這個例子裡面前面兩個演算法類的內容都不需要修改,僅僅需要修改的就是將工廠類改為一個上下類,用上下文類來管理演算法對象,同時這裡為了將判斷程式寫在業務邏輯裡面,所以將策略模式和簡單工廠可以結合如下:
1 class Context//以對象管理演算法資源 2 { 3 private: 4 Collection *pCollection=nullptr; 5 public: 6 Context() = default; 7 Context(string type) 8 { 9 if (type == "打八折") 10 pCollection = new CollectionDiscount(0.8); 11 else if (type == "滿300返100") 12 pCollection = new CollectionReturn(300, 100); 13 else 14 throw(invalid_argument("沒有這種折扣策略")); 15 } 16 float ContextInterface(float money) 17 { 18 return pCollection->GetSum(money); 19 } 20 ~Context() { delete pCollection; } 21 };
1 int main() 2 { 3 Context context("打八折"); 4 cout << context.ContextInterface(100) << endl; 5 return 0; 6 }
可以發現上下文類主要做的修改就是,它擁有一個指向一個演算法對象的指針成員變數,也就是說,完全可以在這個類裡面執行演算法運算,然後再把結果傳輸給用戶,然後因為指針是類的成員變數,所以可以很方便的在這個類的析構函數裡面將這個指針所指對象銷毀,就沒沒必要用到智能指針,也使得整個客戶端代碼簡潔明瞭了不少,最後我們也可以神奇地發現客戶端代碼裡面完全沒有出現演算法類,僅僅只出現了上下文類,使得演算法與客戶端完全分離了。
最後總結一下,策略模式定義了一系列演算法的方法,所有演算法都是完成了相同的工作,僅僅是實現不同,它可以通過上下文類以相同的方式調用所有的演算法,最重要的一點就是減少了演算法類和實用演算法類之間的耦合。那什麼時候用策略模式呢,策略模式就是用來封裝演算法的,但在實踐中,可以用它來封裝幾乎任何類型的規則,主要在分析過程中聽到需要不同時間,應用不同的業務規則,就可以考慮用策略模式處理這種變化的可能性。
最後一句,任何需求的變更都是需要成本的,整個程式員之路也是路漫漫其修遠兮。