策略模式
在GOF的《設計模式:可復用面向對象軟件的基礎》一書中對策略模式是這樣說的:定義一系列的算法,把它們一個個封裝起來,並且使它們可相互替換。該模式使得算法可獨立於使用它的客戶而變化。
策略模式為了適應不同的需求,只把變化點封裝了,這個變化點就是實現不同需求的算法,但是,用戶需要知道各種算法的具體情況。就像上面的加班工資,不同的加班情況,有不同的算法。我們不能在程序中將計算工資的算法進行硬編碼,而是能自由的變化的。這就是策略模式。
UML類圖

Strategy:定義所有支持的算法的公共接口。Context使用這個接口來調用某ConcreteStrategy定義的算法;
ConcreteStrategy:實現Strategy接口的具體算法;
Context:使用一個ConcreteStrategy對象來配置;維護一個對Stategy對象的引用,同時,可以定義一個接口來讓Stategy訪問它的數據。
使用場合
當存在以下情況時使用Strategy模式:
- 許多相關的類僅僅是行為有異。“策略”提供了一種用多個行為中的一個行為來配置一個類的方法;
- 需要使用一個算法的不同變體;
- 算法使用客戶不應該知道的數據。可使用策略模式以避免暴露復雜的、與算法相關的數據結構;
- 一個類定義了多種行為,並且這些行為在這個類的操作中以多個條件語句的形式出現。將相關的條件分支移入它們各自的Strategy類中以替代這些條件語句。(是不是和狀態模式有點一樣哦?)
代碼實現
首先實現最單純的策略模式,代碼如下:
1 #include <iostream> 2 using namespace std; 3 4 // The abstract strategy 5 class Strategy 6 { 7 public: 8 virtual void AlgorithmInterface() = 0; 9 }; 10 11 class ConcreteStrategyA : public Strategy 12 { 13 public: 14 void AlgorithmInterface() 15 { 16 cout<<"I am from ConcreteStrategyA."<<endl; 17 } 18 }; 19 20 class ConcreteStrategyB : public Strategy 21 { 22 public: 23 void AlgorithmInterface() 24 { 25 cout<<"I am from ConcreteStrategyB."<<endl; 26 } 27 }; 28 29 class ConcreteStrategyC : public Strategy 30 { 31 public: 32 void AlgorithmInterface() 33 { 34 cout<<"I am from ConcreteStrategyC."<<endl; 35 } 36 }; 37 38 class Context 39 { 40 public: 41 Context(Strategy *pStrategyArg) : pStrategy(pStrategyArg) 42 { 43 } 44 void ContextInterface() 45 { 46 pStrategy->AlgorithmInterface(); 47 } 48 private: 49 Strategy *pStrategy; 50 }; 51 52 int main() 53 { 54 // Create the Strategy 55 Strategy *pStrategyA = new ConcreteStrategyA; 56 Strategy *pStrategyB = new ConcreteStrategyB; 57 Strategy *pStrategyC = new ConcreteStrategyC; 58 Context *pContextA = new Context(pStrategyA); 59 Context *pContextB = new Context(pStrategyB); 60 Context *pContextC = new Context(pStrategyC); 61 pContextA->ContextInterface(); 62 pContextB->ContextInterface(); 63 pContextC->ContextInterface(); 64 65 if (pStrategyA) delete pStrategyA; 66 if (pStrategyB) delete pStrategyB; 67 if (pStrategyC) delete pStrategyC; 68 69 if (pContextA) delete pContextA; 70 if (pContextB) delete pContextB; 71 if (pContextC) delete pContextC; 72 }
在實際操作的過程中,我們會發現,在main函數中,也就是在客戶端使用策略模式時,會創建非常多的Strategy,而這樣就莫名的增加了客戶端的壓力,讓客戶端的復雜度陡然增加了。那么,我們就可以借鑒簡單工廠模式,使策略模式和簡單工廠模式相結合,從而減輕客戶端的壓力,代碼實現如下:
1 #include <iostream> 2 using namespace std; 3 4 // Define the strategy type 5 typedef enum StrategyType 6 { 7 StrategyA, 8 StrategyB, 9 StrategyC 10 }STRATEGYTYPE; 11 12 // The abstract strategy 13 class Strategy 14 { 15 public: 16 virtual void AlgorithmInterface() = 0; 17 virtual ~Strategy() = 0; // 謝謝hellowei提出的bug,具體可以參見評論 18 }; 19 20 Strategy::~Strategy() 21 {} 22 23 class ConcreteStrategyA : public Strategy 24 { 25 public: 26 void AlgorithmInterface() 27 { 28 cout << "I am from ConcreteStrategyA." << endl; 29 } 30 31 ~ConcreteStrategyA(){} 32 }; 33 34 class ConcreteStrategyB : public Strategy 35 { 36 public: 37 void AlgorithmInterface() 38 { 39 cout << "I am from ConcreteStrategyB." << endl; 40 } 41 42 ~ConcreteStrategyB(){} 43 }; 44 45 class ConcreteStrategyC : public Strategy 46 { 47 public: 48 void AlgorithmInterface() 49 { 50 cout << "I am from ConcreteStrategyC." << endl; 51 } 52 53 ~ConcreteStrategyC(){} 54 }; 55 56 class Context 57 { 58 public: 59 Context(STRATEGYTYPE strategyType) 60 { 61 switch (strategyType) 62 { 63 case StrategyA: 64 pStrategy = new ConcreteStrategyA; 65 break; 66 67 case StrategyB: 68 pStrategy = new ConcreteStrategyB; 69 break; 70 71 case StrategyC: 72 pStrategy = new ConcreteStrategyC; 73 break; 74 75 default: 76 break; 77 } 78 } 79 80 ~Context() 81 { 82 if (pStrategy) delete pStrategy; 83 } 84 85 void ContextInterface() 86 { 87 if (pStrategy) 88 pStrategy->AlgorithmInterface(); 89 } 90 91 private: 92 Strategy *pStrategy; 93 }; 94 95 int main() 96 { 97 Context *pContext = new Context(StrategyA); 98 pContext->ContextInterface(); 99 100 if (pContext) delete pContext; 101 }
在上面這個代碼中,其實,我們可能看到的更多的是簡單工廠模式的應用,我們將策略模式將簡單工廠模式結合在了一起,讓客戶端使用起來更輕松。
總結
策略模式和狀態模式,是大同小異的;狀態模式講究的是狀態的變化,和不同狀態下,執行的不同行為;而策略模式側重於同一個動作,實現該行為的算法的不同,不同的策略封裝了不同的算法。策略模式適用於實現某一功能,而實現該功能的算法是經常改變的情況。在實際工作中,遇到了實際的場景,可能會有更深的體會。比如,我們做某一個系統,該系統可以適用於各種數據庫,我們都知道,連接某一種數據庫的方式是不一樣的,也可以說,連接數據庫的“算法”都是不一樣的。這樣,我們就可以使用策略模式來實現不同的連接數據庫的策略,從而實現數據庫的動態變換。
