標籤:協議 修改 osal rate 部分 依賴 .net img 開始
原文:C#設計模式之十九策略模式(Stragety Pattern)【行為型】
一、引言
今天我們開始講“行為型”設計模式的第七個模式,該模式是【策略模式】,英文名稱是:Stragety Pattern。在現實生活中,策略模式的例子也非常常見,例如,在一個公司中,會有各種工作人員,比如:有的是普通員工,有的是軟體架構師,有的是部門經理,當然也會有公司的CEO。這些工作人員負責的工作不同,擔負的責任不同,自然得到的報酬也就不同了。每種工作人員都有自己的工資,但是每個工種的工作人員的工資的計算方法又是不一樣的。如果所有人的工資都一樣,肯定會天下大亂的。如果不採用策略模式來實現這個需求的話,我們可能會這樣來做,我們會定義一個工資類,該類有一個屬性來標識工作人員的類型,並且有一個計算工資的CalculateSalary()方法,在該方法體內需要對工作人員類型進行判斷,通過if-else語句來針對不同的工作人員類型來計算其所得工資。這樣的實現確實可以解決這個情境,但是這樣的設計不利於擴充,如果系統後期需要增加一種新的工種時,此時不得不回去修改CalculateSalary方法來多添加一個判斷語句,這樣明顯違背了“開放——封閉”原則。此時,我們可以考慮使用原則模式來解決這個問題,既然工資計算方法是這個情境中的變化部分,此時自然可以想到對工資演算法進行抽象,不同工種的工資可以用不用的策略演算法具體實現,想要得到某個工作人員的工資,用其相應的工資演算法策略來計算就可以了。
二、策略模式的詳細介紹
2.1、動機(Motivate)
在軟體構建過程中,某些對象使用的演算法可能多種多樣,經常改變,如果將這些演算法都編碼到對象中,將會使對象變得異常複雜;而且有時候支援不使用的演算法也是一個效能負擔。如何在運行時根據需要透明地更改對象的演算法?將演算法與對象本身解耦,從而避免上述問題?
2.2、意圖(Intent)
定義一系列演算法,把它們一個個封裝起來,並且使它們可互相替換。該模式使得演算法可獨立於使用它的客戶而變化。 ——《設計模式》GoF
2.3、結構圖(Structure)
2.4、模式的組成
可以看出,在策略模式的結構圖有以下角色:
(1)、環境角色(Context):持有一個Strategy類的引用。
需要使用ConcreteStrategy提供的演算法。
內部維護一個Strategy的執行個體。
負責動態設定運行時Strategy具體的實現演算法。
負責跟Strategy之間的互動和資料傳遞
(2)、抽象策略角色(Strategy):定義了一個公用介面,各種不同的演算法以不同的方式實現這個介面,Context使用這個介面調用不同的演算法,一般使用介面或抽象類別實現。
(3)、具體策略角色(ConcreteStrategy):實現了Strategy定義的介面,提供具體的演算法實現。
2.5、策略模式的代碼實現
在現實生活中,策略模式的例子也是很多的,例如:一個公司會有很多工作種類,每個工作種類負責的工作不同,自然每個工種的工資計算方法也會有千差萬別,我們今天就以工資的計算為例來說明策略模式的使用,我們直接上代碼,但是實際編碼中切記別這樣,我們要通過迭代的方式使用模式。實現代碼如下:
1 namespace 策略模式的實現 2 { 3 //環境角色---相當於Context類型 4 public sealed class SalaryContext 5 { 6 private ISalaryStrategy _strategy; 7 8 public SalaryContext(ISalaryStrategy strategy) 9 {10 this._strategy = strategy;11 }12 13 public ISalaryStrategy ISalaryStrategy14 {15 get { return _strategy; }16 set { _strategy = value; }17 }18 19 public void GetSalary(double income)20 {21 _strategy.CalculateSalary(income);22 }23 }24 25 //抽象策略角色---相當於Strategy類型26 public interface ISalaryStrategy27 {28 //工資計算29 void CalculateSalary(double income);30 }31 32 //程式員的工資--相當於具體策略角色ConcreteStrategyA33 public sealed class ProgrammerSalary : ISalaryStrategy34 {35 public void CalculateSalary(double income)36 {37 Console.WriteLine("我的工資是:基本工資(" + income + ")底薪(" + 8000 + ")+加班費+項目獎金(10%)");38 }39 }40 41 //普通員工的工資---相當於具體策略角色ConcreteStrategyB42 public sealed class NormalPeopleSalary : ISalaryStrategy43 {44 public void CalculateSalary(double income)45 {46 Console.WriteLine("我的工資是:基本工資(" + income + ")底薪(3000)+加班費");47 }48 }49 50 //CEO的工資---相當於具體策略角色ConcreteStrategyC51 public sealed class CEOSalary : ISalaryStrategy52 {53 public void CalculateSalary(double income)54 {55 Console.WriteLine("我的工資是:基本工資(" + income + ")底薪(20000)+項目獎金(20%)+公司股票");56 }57 }58 59 60 public class Client61 {62 public static void Main(String[] args)63 {64 //普通員工的工資65 SalaryContext context = new SalaryContext(new NormalPeopleSalary());66 context.GetSalary(3000);67 68 //CEO的工資69 context.ISalaryStrategy = new CEOSalary();70 context.GetSalary(6000);71 72 Console.Read();73 }74 }75 }
三、策略模式的實現要點:
Strategy及其子類為組件提供了一系列可重用的演算法,從而可以使得類型在運行時方便地根據需要在各個演算法之間進行切換,所謂封裝演算法,支援演算法的變化。Strategy模式提供了用條件判斷語句以外的另一種選擇,消除條件判斷語句,就是在解耦合。含有許多條件判斷語句的代碼通常都需要Strategy模式。
與State類似,如果Strategy對象沒有執行個體變數,那麼各個上下文可以共用一個Strategy對象,從而節省對象開銷。Strategy模式適用的是演算法結構中整個演算法的改變,而不是演算法中某個部分的改變。
Template Method方法:執行演算法的步驟協議是本身放在抽象類別裡面的,允許一個通用的演算法操作多個可能實現
Strategy模式:執行演算法的協議是在具體類,每個具體實現有不同通用演算法來做。
(1)、策略模式的主要優點有:
1】、策略類之間可以自由切換。由於策略類都實現同一個介面,所以使它們之間可以自由切換。
2】、易於擴充。增加一個新的策略只需要添加一個具體的策略類即可,基本不需要改變原有的代碼。
3】、避免使用多重條件選擇語句,充分體現物件導向設計思想。
(2)、策略模式的主要缺點有:
1】、用戶端必須知道所有的策略類,並自行決定使用哪一個策略類。這點可以考慮使用IOC容器和依賴注入的方式來解決,關於IOC容器和依賴注入(Dependency Inject)的文章可以參考:IoC 容器和Dependency Injection 模式。
2】、策略模式會造成很多的策略類。
(3)、在下面的情況下可以考慮使用原則模式:
1】、一個系統需要動態地在幾種演算法中選擇一種的情況下。那麼這些演算法可以封裝到一個個具體的演算法類裡面,並為這些具體的演算法類提供一個統一的介面。
2】、如果一個對象有很多的行為,如果不使用合適的模式,這些行為就只好使用多重的if-else語句來實現,此時,可以使用原則模式,把這些行為轉移到相應的具體策略類裡面,就可以避免使用難以維護的多重條件選擇語句,並體現物件導向涉及的概念。
四、.NET 策略模式的實現
在.NET Framework中也不乏策略模式的應用例子。例如,在.NET中,為集合類型ArrayList和List<T>提供的排序功能,其中實現就利用了策略模式,定義了IComparer介面來對比較演算法進行封裝,實現IComparer介面的類可以是順序,或逆序地比較兩個對象的大小,具體.NET中的實現可以使用反編譯工具查看List<T>.Sort(IComparer<T>)的實現。其中List<T>就是承擔著環境角色,而IComparer<T>介面承擔著抽象策略角色,具體的策略角色就是實現了IComparer<T>介面的類,List<T>類本身實現了存在實現了該介面的類,我們可以自訂繼承與該介面的具體策略類。
五、總結
今天開始有點晚,寫完也比較晚。策略模式不是很難,可以說很簡單,或許大家已經在實際編碼中使用過該模式了。還是老話,我們要向清楚的使用每一個模式,要理解他們的優缺點,要深刻理解他們使用的場合。我們使用模式切記不能上來就使用模式,我們應該通過迭代的方式來寫代碼。我們編碼的時候,第一印象很重要,第一次怎麼想的就怎麼寫,如果有需求的改變,且改變比較頻繁,然後我們仔細分析變化點,再找合適的模式來解決相應的問題。
C#設計模式之十九策略模式(Stragety Pattern)【行為型】