關於 Flyweight 本篇將討論以下幾個問題
1. 關於 Flyweight
2. UML
3. 將 UML 轉為程式碼
4. 情境
測試環境:
OS:Windows 10
IDE:Visual Studio 2019
1. 關於 Flyweight
Use sharing to support large numbers of fine-grained objects efficiently.
by Gang of Four
- 用來有效率的共享大量細粒度的對象
Flyweight(享元)屬於結構型(Structural Patterns),當遇到系統中頻繁創建且只會異動狀態而不改變物件本身的小物件時,可藉由 Flyweight 來緩存物件,提供不同呼叫端重複使用,達到節省記憶體的目的。
優點:
- 藉由共用相似物件來達到節省記憶體
缺點:
- 使用 Flyweight 的物件存於緩存中,就算沒使用也不會被 GC 回收
2. UML
Class 間關聯:
- FlyweightFactory 可包含 Flyweight
- Client 關聯 FlyweightFactory & ConcreteFlyweight
- ConcreteFlyweight 繼承 Flyweight
Class:
- Client:呼叫端
- FlyweightFactory:以工廠方法實作享元工廠
- Flyweight:享元的抽象類別或介面
- ConcreteFlyweight:享元實作
3. 將 UML 轉為程式碼
享元介面
/// <summary>
/// 享元介面
/// </summary>
public interface IFlyweight
{
void Operation(int extrinsicState);
}
享元工廠
/// <summary>
/// 享元工廠
/// </summary>
public class FlyweightFactory
{
private Hashtable flyweights = new Hashtable();
public FlyweightFactory()
{
flyweights.Add("One", new ConcreteFlyweight());
flyweights.Add("Two", new ConcreteFlyweight());
flyweights.Add("Three", new ConcreteFlyweight());
}
public IFlyweight GetFlyweight(string key)
{
return ((IFlyweight)flyweights[key]);
}
}
享元實作
/// <summary>
/// 享元實作
/// </summary>
public class ConcreteFlyweight : IFlyweight
{
public void Operation(int extrinsicState)
{
Console.WriteLine($"ConcreteFlyweight: {extrinsicState}");
}
}
- 建立享元工廠
factory
- 取得享元工廠中已建立實體
- 操作享元物件
static void Main(string[] args)
{
int extrinsicstate = 0;
Default.FlyweightFactory factory = new Default.FlyweightFactory();
Default.IFlyweight flyweight_1 = factory.GetFlyweight("One");
flyweight_1.Operation(++extrinsicstate);
Default.IFlyweight flyweight_2 = factory.GetFlyweight("Two");
flyweight_2.Operation(++extrinsicstate);
Default.IFlyweight flyweight_3 = factory.GetFlyweight("Three");
flyweight_3.Operation(++extrinsicstate);
Console.ReadLine();
}
執行結果
ConcreteFlyweight: 1
ConcreteFlyweight: 2
ConcreteFlyweight: 3
4. 情境
我們接到了一個提供線上商城客戶結帳時轉換匯率的需求
- 線上商城客戶很多,每個客戶都要 new 匯率 class 很吃記憶體
- 匯率固定提供部分貨幣類別,介面相同
貨幣類別 Enum
/// <summary>
/// 貨幣類別
/// </summary>
public enum CurrencyType
{
TWD,
JPY,
USD,
EUR,
}
實作匯率工廠,並在建構子提供對應匯率
/// <summary>
/// 匯率工廠
/// </summary>
public class ExchangeRatesFactory
{
private Hashtable flyweights = new Hashtable();
public ExchangeRatesFactory()
{
flyweights.Add(CurrencyType.TWD, new ExchangeRates((decimal)1));
flyweights.Add(CurrencyType.JPY, new ExchangeRates((decimal)0.273));
flyweights.Add(CurrencyType.USD, new ExchangeRates((decimal)28.235));
flyweights.Add(CurrencyType.EUR, new ExchangeRates((decimal)34.35));
}
public IExchangeRates GetConverter(CurrencyType currencyType)
{
return ((IExchangeRates)flyweights[currencyType]);
}
}
轉換介面
/// <summary>
/// 轉換介面
/// </summary>
public interface IExchangeRates
{
public decimal Converter(int amount);
}
傳換實作
/// <summary>
/// 傳換實作
/// </summary>
public class ExchangeRates : IExchangeRates
{
private decimal _CashRate { get; }
public ExchangeRates (decimal cashRate)
{
_CashRate = cashRate;
}
public decimal Converter(int amount)
{
var newAmount = amount / _CashRate;
return decimal.Parse(newAmount.ToString("#0.00"));
}
}
- 建立享元工廠
factory
- 取得享元物件(匯率轉換)
- 透過享元物件計算金額
static void Main(string[] args)
{
Situation.ExchangeRatesFactory factory = new Situation.ExchangeRatesFactory();
int amount = 100;
Situation.IExchangeRates converter_JPY = factory.GetConverter(Situation.CurrencyType.JPY);
var jpy = converter_JPY.Converter(amount);
Console.WriteLine($"TWD 轉換至 JPY:{amount} → {jpy}");
Situation.IExchangeRates converter_USD = factory.GetConverter(Situation.CurrencyType.USD);
var usd = converter_USD.Converter(amount);
Console.WriteLine($"TWD 轉換至 USD:{amount} → {usd}");
Situation.IExchangeRates converter_EUR = factory.GetConverter(Situation.CurrencyType.EUR);
var eur = converter_EUR.Converter(amount);
Console.WriteLine($"TWD 轉換至 EUR:{amount} → {eur}");
Console.ReadLine();
}
執行結果
TWD 轉換至 JPY:100 → 366.30
TWD 轉換至 USD:100 → 3.54
TWD 轉換至 EUR:100 → 2.91
完整程式碼
GitHub:Structural_06_Flyweight