關於 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
- 定義並封裝一系列演算法,並使它們可切換
- 策略模式使演算法可依據呼叫端指定而抽換
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 轉為程式碼
策略介面
/// <summary>
/// 策略介面
/// </summary>
public interface IStrategy
{
public void AlgorithmInterface();
}
策略實作 A
/// <summary>
/// 策略實作 A
/// </summary>
public class ConcreteStrategyA : IStrategy
{
public void AlgorithmInterface()
{
Console.WriteLine("Called ConcreteStrategyA.AlgorithmInterface()");
}
}
策略實作 B
/// <summary>
/// 策略實作 B
/// </summary>
public class ConcreteStrategyB : IStrategy
{
public void AlgorithmInterface()
{
Console.WriteLine("Called ConcreteStrategyB.AlgorithmInterface()");
}
}
策略實作 C
/// <summary>
/// 策略實作 C
/// </summary>
public class ConcreteStrategyC : IStrategy
{
public void AlgorithmInterface()
{
Console.WriteLine("Called ConcreteStrategyC.AlgorithmInterface()");
}
}
使用演算法的物件
/// <summary>
/// 使用演算法的物件
/// </summary>
public class Context
{
private IStrategy _strategy;
public Context(IStrategy strategy)
{
_strategy = strategy;
}
public void ContextInterface()
{
_strategy.AlgorithmInterface();
}
}
- 建立
context
並指定策略實作 A / B / C - 呼叫策略實作 A / B / 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();
}
執行結果
Called ConcreteStrategyA.AlgorithmInterface()
Called ConcreteStrategyB.AlgorithmInterface()
Called ConcreteStrategyC.AlgorithmInterface()
4. 情境
我們接到了一個使用者在線上商城可自行選擇折扣方案的需求
- 同時有多種折扣方案時,使用者可自行選定一種
策略介面
/// <summary>
/// 策略介面
/// </summary>
public interface IStrategy
{
public int Discount(int amount);
}
策略實作,拆分各種計算結帳金額邏輯
/// <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;
}
}
依據使用者選擇折扣計算結帳金額
/// <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}");
}
}
- 建立
context
並選定折扣 - 輸入結帳金額計算折扣
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();
}
執行結果
原價:1600
結帳金額九折
折扣價:1440
原價:1100
滿千折百
折扣價:1000
原價:600
送贈品
折扣價:600
完整程式碼
GitHub:Behavioral_09_Strategy