# 22. Strategy ###### tags: `DesignPatterns` ## 關於 Strategy 本篇將討論以下幾個問題 > ### 1. 關於 Strategy > ### 2. UML > ### 3. 將 UML 轉為程式碼 > ### 4. 情境 --- ## 測試環境: >OS:Windows 10 >IDE:Visual Studio 2019 --- ## 1. 關於 Strategy > Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it. > > by [Gang of Four](https://en.wikipedia.org/wiki/Design_Patterns) - 定義並封裝一系列演算法,並使它們可切換 - 策略模式使演算法可依據呼叫端指定而抽換 Strategy(策略)屬於行為型(Behavioral Patterns),當遇到**一段程式其中有部分邏輯可由外部指定** 時,可藉由 Strategy 將邏輯拆分成獨立 class,之後新增邏輯時只需新增 Strategy 實作即可。 優點: - 符合 開閉原則(Open Closed Principle) 缺點: - 所有策略實作都需要暴露至呼叫端 --- ## 2. UML  Class 間關聯: - Context 可包含 Strategy - ConcreteStrategy A/B/C 繼承 Strategy Class: - Context:使用演算法的物件 - Strategy:策略的抽象類別或介面 - ConcreteStrategy:策略實作 --- ## 3. 將 UML 轉為程式碼 策略介面 ```C# /// <summary> /// 策略介面 /// </summary> public interface IStrategy { public void AlgorithmInterface(); } ``` 策略實作 A ```C# /// <summary> /// 策略實作 A /// </summary> public class ConcreteStrategyA : IStrategy { public void AlgorithmInterface() { Console.WriteLine("Called ConcreteStrategyA.AlgorithmInterface()"); } } ``` 策略實作 B ```C# /// <summary> /// 策略實作 B /// </summary> public class ConcreteStrategyB : IStrategy { public void AlgorithmInterface() { Console.WriteLine("Called ConcreteStrategyB.AlgorithmInterface()"); } } ``` 策略實作 C ```C# /// <summary> /// 策略實作 C /// </summary> public class ConcreteStrategyC : IStrategy { public void AlgorithmInterface() { Console.WriteLine("Called ConcreteStrategyC.AlgorithmInterface()"); } } ``` 使用演算法的物件 ```C# /// <summary> /// 使用演算法的物件 /// </summary> public class Context { private IStrategy _strategy; public Context(IStrategy strategy) { _strategy = strategy; } public void ContextInterface() { _strategy.AlgorithmInterface(); } } ``` 1. 建立`context`並指定策略實作 A / B / C 2. 呼叫策略實作 A / B / C ```C# static void Main(string[] args) { Default.Context context; context = new Default.Context(new Default.ConcreteStrategyA()); context.ContextInterface(); context = new Default.Context(new Default.ConcreteStrategyB()); context.ContextInterface(); context = new Default.Context(new Default.ConcreteStrategyC()); context.ContextInterface(); Console.ReadLine(); } ``` 執行結果 ```Console Called ConcreteStrategyA.AlgorithmInterface() Called ConcreteStrategyB.AlgorithmInterface() Called ConcreteStrategyC.AlgorithmInterface() ``` --- ## 4. 情境 我們接到了一個使用者在線上商城可自行選擇折扣方案的需求 - 同時有多種折扣方案時,使用者可自行選定一種 策略介面 ```C# /// <summary> /// 策略介面 /// </summary> public interface IStrategy { public int Discount(int amount); } ``` 策略實作,拆分各種計算結帳金額邏輯 ```C# /// <summary> /// 結帳金額九折 /// </summary> public class TenPercentOff : IStrategy { public int Discount(int amount) { Console.WriteLine("結帳金額九折"); return (int)(amount * 0.9); } } /// <summary> /// 滿千折百 /// </summary> public class ThousandGet100CashBack : IStrategy { public int Discount(int amount) { Console.WriteLine("滿千折百"); return amount >= 1000 ? amount - 100 : amount; } } /// <summary> /// 送贈品 /// </summary> public class Giveaway : IStrategy { public int Discount(int amount) { Console.WriteLine("送贈品"); return amount; } } ``` 依據使用者選擇折扣計算結帳金額 ```C# /// <summary> /// 依據使用者選擇折扣計算結帳金額 /// </summary> public class GetPrice { private IStrategy _strategy; public GetPrice(IStrategy strategy) { _strategy = strategy; } public void Calculate(int amount) { Console.WriteLine($"原價:{amount}"); var newAmount = _strategy.Discount(amount); Console.WriteLine($"折扣價:{newAmount}"); } } ``` 1. 建立`context`並選定折扣 2. 輸入結帳金額計算折扣 ```C# static void Main(string[] args) { Situation.GetPrice context; context = new Situation.GetPrice(new Situation.TenPercentOff()); context.Calculate(1600); Console.WriteLine("\n"); context = new Situation.GetPrice(new Situation.ThousandGet100CashBack()); context.Calculate(1100); Console.WriteLine("\n"); context = new Situation.GetPrice(new Situation.Giveaway()); context.Calculate(600); Console.ReadLine(); } ``` 執行結果 ```Console 原價:1600 結帳金額九折 折扣價:1440 原價:1100 滿千折百 折扣價:1000 原價:600 送贈品 折扣價:600 ``` --- ## 完整程式碼 GitHub:[Behavioral_09_Strategy](https://github.com/darionnnnnn/blog/tree/master/Blog/Behavioral_09_Strategy) --- ## 總結 ### 在初期策略數量少且不知道未來會不會增加時使用 Strategy 會提高程式複雜度,建議直接使用 [Delegates](https://docs.microsoft.com/zh-tw/dotnet/csharp/programming-guide/delegates/) 來傳入,等到策略數量增加時再使用 Strategy 重構即可。 --- ## 參考資料 1. [Design Patterns](https://en.wikipedia.org/wiki/Design_Patterns) 2. [大話設計模式](https://www.tenlong.com.tw/products/9789866761799) 2. [dofactory](https://www.dofactory.com/) 3. [Refactoring.Guru](https://refactoring.guru/) --- ## 新手上路,若有錯誤還請告知,謝謝
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up