? 所謂策略模式(Strategy Pattern),就是將策略 (算法) 封裝為一個對象,易于相互替換,如同 USB 設備一樣可即插即用;如果將策略、具體的算法和行為,編碼在某個類或客戶程序內部,將導至事后的修改和擴展不易。
? 當有多種「策略」時,通常的作法就是將這些個策略,和這些策略的算法、行為,分別封裝在各個類中,并讓這些類,去繼承某個公用的抽象類或接口。接著在客戶 程序中,就可動態引用,且易于更換這些不同的「策略」,不會因為日后添加、修改了某一個「策略」,就得重新修改、編譯多處的源代碼。此即為一種「封裝變化 點」的做法,將常會變化的部分進行抽象、定義為接口,亦即實現「面向接口編程」的概念。且客戶程序 (調用者) 只須知道接口的外部定義即可,具體的實現則無須理會。
?? 策略模式(Strategy Pattern)在外形上與狀態模式很相似,但在意圖上有些不同。其意圖是使這些算法可以相互替換,并提供一種方法來選擇最合適的算法。
?? 策略模式(Strategy Pattern)的UML圖如下:
????????????????????????
在策略模式里主要有三種角色:環境角色、抽象策略角色和具體策略角色。
1、環境(Context)角色:持有一個抽象策略(Strategy)角色的引用。也叫上下文。
2、抽象策略(Strategy)角色:這是一個抽象角色,通常由一個接口或一個抽象類來實現。
3、具體策略(ConcreteStrategy)角色:包裝了相應的算法和行為。
下面我們用代碼來示例策略模式,程序如下圖:
????????????????????????
一、策略模式基本思路示例
1、環境(Context)角色:


using?System;
using?System.Collections.Generic;
using?System.Linq;
using?System.Text;
namespace?MyStrategyPattern
{
????#region?定義Context類
????class?Context
????{
????????private?Strategy?_strategy;
????????#region?構造函數
????????public?Context(Strategy?strategy)
????????{
????????????this._strategy?=?strategy;
????????}
????????#endregion
????????#region?定義算法接口
????????//具體的算法由傳入的strategy對象的AlgorithmInterface方法來實現
????????public?void?ContextInterface()
????????{
????????????_strategy.AlgorithmInterface();
????????}
????????#endregion
????}
????#endregion
}


using?System;
using?System.Collections.Generic;
using?System.Linq;
using?System.Text;
namespace?MyStrategyPattern
{
????#region?抽象策略類Strategy,定義了具體策略類的共有算法接口
????abstract??class?Strategy
????{
????????public?abstract?void?AlgorithmInterface();
????}
????#endregion
}


using?System;
using?System.Collections.Generic;
using?System.Linq;
using?System.Text;
namespace?MyStrategyPattern
{?
????//定義了一系列的具體策略類,它們繼承自抽象策略類
????class?ConcreteStrategyA?:?Strategy
????{
????????public?override?void?AlgorithmInterface()
????????{
????????????Console.WriteLine("使用了算法A來處理Context對象");
????????}
????}
????class?ConcreteStrategyB?:?Strategy
????{
????????public?override?void?AlgorithmInterface()
????????{
????????????Console.WriteLine("使用了算法B來處理Context對象");
????????}
????}
????class?ConcreteStrategyC?:?Strategy
????{
????????public?override?void?AlgorithmInterface()
????????{
????????????Console.WriteLine("使用了算法C來處理Context對象");
????????}
????}
}


????????????#region?基本思路示例
????????????Console.WriteLine("----策略模式基本思路示例----");
????????????Context?context;
????????????//調用不同的算法來處理對象,算法的差異在Context傳參時(new?ConcreteStrategyA())決定
????????????context?=?new?Context(new?ConcreteStrategyA());
????????????context.ContextInterface();
????????????context?=?new?Context(new?ConcreteStrategyB());
????????????context.ContextInterface();
????????????context?=?new?Context(new?ConcreteStrategyC());
????????????context.ContextInterface();
????????????#endregion
二、求和計算的策略模式示例
1、環境(Context)角色:CalculateContext.


using?System;
using?System.Collections.Generic;
using?System.Linq;
using?System.Text;
namespace?MyStrategyPattern
{
????class?CalculateContext
????{
????????ICalculateStrategy?_strategy;
????????public?CalculateContext(ICalculateStrategy?strategy)
????????{
????????????_strategy?=?strategy;
????????}
????????public?void?PerformCalculation(List<int>?list)
????????{
????????????Console.WriteLine(string.Format("對列表中所有整數求和,結果為:{0}",?_strategy.Sum(list)));
????????}
????}
}
2、抽象策略(Strategy)角色:ICalculateStrategy


using?System;
using?System.Collections.Generic;
using?System.Linq;
using?System.Text;
namespace?MyStrategyPattern
{
????interface??ICalculateStrategy
????{
????????int?Sum(List<int>?list);
????}
}
3、具體策略(ConcreteStrategy)角色:CalculateStrategies


using?System;
using?System.Collections.Generic;
using?System.Linq;
using?System.Text;
namespace?MyStrategyPattern
{
????#region?方法A
????class?ConcreteCalculateStrategyA?:?ICalculateStrategy
????{
????????//利用List的Sum功能對List中整數求和
????????public?int?Sum(List<int>?list)
????????{
????????????Console.WriteLine("----。使用方法A進行求和----");
????????????return?list.Sum();
????????}
????}
????#endregion
????#region?方法B
????class?ConcreteCalculateStrategyB?:?ICalculateStrategy
????{
????????//使用傳統的遍歷方法,對List中整數求和
????????public?int?Sum(List<int>?list)
????????{
????????????Console.WriteLine("----。使用方法B進行求和----");
????????????int?result?=?0;
????????????foreach(int?value?in?list)
????????????{
????????????????result?+=?value;
????????????}
????????????return?result;
????????}
????}
????#endregion
}
4、客戶應用代碼


???????????#region?求和計算的策略模式示例
????????????Console.WriteLine("\n\n----求和計算的策略模式示例----");
????????????List<int>?lst?=?new?List<int>();
????????????lst.Add(3);
????????????lst.Add(6);
????????????lst.Add(8);
????????????lst.Add(9);
????????????CalculateContext?caltext;
????????????caltext?=?new?CalculateContext(new?ConcreteCalculateStrategyA());
????????????caltext.PerformCalculation(lst);
????????????caltext?=?new?CalculateContext(new?ConcreteCalculateStrategyB());
????????????caltext.PerformCalculation(lst);
????????????Console.ReadKey();
????????????#endregion
效果如下圖:
????????????????????????
總結:
Strategy Pattern 適用的情景:
1、應用中的許多類,在解決某些問題時很相似,但實現的行為有所差異。比如:不同功能的程序,都可能要用到「排序」算法。
2、根據運行環境的不同,需要采用不同的算法。比如:在手機、PC 計算機上,因硬件等級不同,必須采用不同的排序算法。
3、針對給定的目的,存在多種不同的算法,且我們可用代碼實現算法選擇的標準。
4、需要封裝復雜的數據結構。比如:特殊的加密算法,客戶程序僅需要知道調用的方式即可。
5、同上,算法中的羅輯和使用的數據,應該與客戶程序隔離時。
Strategy Pattern 的優點:
1、簡化了單元測試,因為每個算法都有自己的類,可以通過自己的接口單獨做測試。
2、避免程序中使用多重條件轉移語句,使系統更靈活,并易于擴展。
3、高內聚、低偶合。
Strategy Pattern 的缺點:
1、因為每個具體策略都會產生一個新類,所以會增加需要維護的類的數量。
2、選擇所用具體實現的職責由客戶程序承擔,并轉給 Context 對象,并沒有解除客戶端需要選擇判斷的壓力。
3、若要減輕客戶端壓力,或程序有特殊考量,還可把 Strategy 與 Simple Factory 兩種 Pattern 結合,即可將選擇具體算法的職責改由 Context 來承擔,亦即將具體的算法,和客戶程序做出隔離。