[leetcode] 40. Combination Sum II
阿新 • • 發佈:2022-03-30
策略模式
GOF中定義
定義一系列演算法,把它們一個個封裝起來,並且使它們可互相替換(變化)。該模式使得演算法可獨立於使用它的客戶程式(穩定)而變化(擴充套件,子類化)。
動機
-
在軟體構建過程中,某些物件使用的演算法可能多種多樣,經常改變,如果將這些演算法都編碼到物件中,將會使物件變得異常複雜;而且有時候支援不使用的演算法也是一個性能負擔。
-
如何在執行時根據需要透明的更改物件的演算法?將演算法與物件本身解耦,從而避免上述問題?
示例
計算稅收,不同國家有不同稅收的計算方法,需要分別對不同的計算方法使用不同的演算法模型。
//列舉型別 enum TaxBase { CN_Tax, US_Tax, DE_Tax }; class SalesOrder{ TaxBase tax; public: double CalculateTax(){ //... if (tax == CN_Tax){ //CN*********** } else if (tax == US_Tax){ //US*********** } else if (tax == DE_Tax){ //DE*********** } //.... } };
在本例中,將所有稅收的計算方法都置於SalesOrder的CalculateTax()中,這樣會帶來兩個矛盾:
- 若增加其他國家稅收的計算方法,是在程式本身增加程式碼,違反開閉原則,且改動處較多。改動處:列舉型別內部新增FR_Tax和CalculateTax()中增加else if (tax == FR_Tax){...};
- 若新增的稅收計算方法較多,則會使得CalculateTax()方法變得臃腫,並且很可能會包含使用頻率很低的計算方法。
那麼不再使用列舉型別來實現,看下面程式碼
class TaxStrategy{ public: virtual double Calculate(const Context& context)=0; virtual ~TaxStrategy(){} }; class CNTax : public TaxStrategy{ public: virtual double Calculate(const Context& context){ //*********** } }; class USTax : public TaxStrategy{ public: virtual double Calculate(const Context& context){ //*********** } }; class DETax : public TaxStrategy{ public: virtual double Calculate(const Context& context){ //*********** } }; //擴充套件 //********************************* class FRTax : public TaxStrategy{ public: virtual double Calculate(const Context& context){ //......... } }; class SalesOrder{ private: TaxStrategy* strategy; public: SalesOrder(StrategyFactory* strategyFactory){ this->strategy = strategyFactory->NewStrategy(); } ~SalesOrder(){ delete this->strategy; } public: double CalculateTax(){ //... Context context(); double val = strategy->Calculate(context); //多型呼叫 //... } };
在本程式碼中,
- 定義一個TaxStrategy類,此類為純虛基類,類中定義純虛擬函式Calculate()方法。
- 分別使用不同的子類繼承基類,並重寫不同國家的稅收計算方法Calculate()方法
- SalesOrder類內部放一個TaxStrategy基類型別指標,便於使用多型機制根據指標型別去呼叫不同國家的稅收計算方法,得到結果。
優點:
-
由於提取處了抽象層TaxStrategy類,並在啟動定義介面Calculate()方法。在後續擴充套件新的稅收計算方法時,直接新擴充套件一個類並在該類中重寫Calculate()即可,如程式中FRTax所示,不需修改原來程式碼,符合開閉原則,便於擴充套件。同時使用增量編譯可以減少編譯過程中的時間成本等。
-
並沒有將所有的計算方法都包含在一個計算方法中,可適當提高計算效能。
做出上述程式的類圖
在上述圖中,Context和Strategy部分是穩定的,而Strategy類的子類重寫了稅收計算方法Calculate(),該方法會隨著專案的需求發生變化,故本部分是變化的。
在應對Calculate()計算方法的變化時,可通過新增Strategy類的子類並重寫Calculate()來實現。
要點總結
- Strategy及其子類為元件提供了一系列可重用的演算法,從而可以使得型別在執行時方便地根據需要在各個演算法之間進行切換。
- Strategy模式提供了用條件判斷語句以外的另一種選擇,消除條件判斷語句,就是在解耦合。含有許多條件判斷語句的程式碼通常都需要Strategy模式。
- 如果Strategy物件沒有例項變數,那麼各個上下文可以共享同一個Strategy物件,從而節省物件開銷。