Pattern: 簡單工廠模式
Class Diagram: 世紀帝國
情境:根據用戶端對兵種的需求,我們要生產對應的軍事單位
1 2 3 4 5 6 7 8 9 namespace DotNetCoreKata.Enums ;public enum UnitCategory{ Unknown = 0 , Military = 1 , Archer = 2 , Knight = 3 , }
兵種有民兵 、弓兵 及騎士 三種類型。
1 2 3 4 5 6 7 namespace DotNetCoreKata.DomainModels.AgeOfEmpires ;public interface IUnit { string Attack () ; string Move () ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 using DotNetCoreKata.DomainModels.AgeOfEmpires.Transportation;using DotNetCoreKata.DomainModels.AgeOfEmpires.Weapon;namespace DotNetCoreKata.DomainModels.AgeOfEmpires ;public class Unit (IWeapon weapon, ITransportation transportation ) : IUnit{ public string Attack () { return $"attacks with {weapon.Name()} " ; } public string Move () { return $"moves using {transportation.Mode()} " ; } }
軍事單位會使用武器 進攻,用對應的移動方式 移動。
1 2 3 4 5 6 namespace DotNetCoreKata.DomainModels.AgeOfEmpires.Weapon ;public interface IWeapon { string Name () ; }
1 2 3 4 5 6 7 8 9 namespace DotNetCoreKata.DomainModels.AgeOfEmpires.Weapon ;public class Stick : IWeapon { public string Name () { return "stick" ; } }
1 2 3 4 5 6 7 8 9 namespace DotNetCoreKata.DomainModels.AgeOfEmpires.Weapon ;public class Bow : IWeapon { public string Name () { return "bow" ; } }
1 2 3 4 5 6 7 8 9 namespace DotNetCoreKata.DomainModels.AgeOfEmpires.Weapon ;public class Sword : IWeapon { public string Name () { return "sword" ; } }
武器有木棒 、弓箭 及長劍 。
1 2 3 4 5 6 namespace DotNetCoreKata.DomainModels.AgeOfEmpires.Transportation ;public interface ITransportation { string Mode () ; }
1 2 3 4 5 6 7 8 9 namespace DotNetCoreKata.DomainModels.AgeOfEmpires.Transportation ;public class Legs : ITransportation { public string Mode () { return "legs" ; } }
1 2 3 4 5 6 7 8 9 namespace DotNetCoreKata.DomainModels.AgeOfEmpires.Transportation ;public class Horse : ITransportation { public string Mode () { return "horse" ; } }
1 2 3 4 5 6 7 8 9 using DotNetCoreKata.DomainModels.AgeOfEmpires;using DotNetCoreKata.Enums;namespace DotNetCoreKata.Services.AgeOfEmpires ;public interface IClient { IUnit Train (UnitCategory unitCategory ) ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 using DotNetCoreKata.DomainModels.AgeOfEmpires;using DotNetCoreKata.DomainModels.AgeOfEmpires.Transportation;using DotNetCoreKata.DomainModels.AgeOfEmpires.Weapon;using DotNetCoreKata.Enums;namespace DotNetCoreKata.Services.AgeOfEmpires ;public class Client : IClient { public IUnit Train (UnitCategory unitCategory ) { Console.Write($"Ask resources to build unit: {unitCategory} ." ); if (unitCategory == UnitCategory.Military) { return new Unit(new Stick(), new Legs()); } else if (unitCategory == UnitCategory.Archer) { return new Unit(new Bow(), new Legs()); } else { return new Unit(new Sword(), new Horse()); } } }
使用簡單工廠模式改造
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 using DotNetCoreKata.Enums;namespace DotNetCoreKata.DomainModels.AgeOfEmpires ;public static class TrainingCenter { public static IUnit Create (UnitCategory unitCategory ) { return unitCategory switch { UnitCategory.Military => new Unit(new Stick(), new Legs()), UnitCategory.Archer => new Unit(new Bow(), new Legs()), UnitCategory.Knight => new Unit(new Sword(), new Horse()), _ => new Unit(new Stick(), new Legs()) }; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 using DotNetCoreKata.DomainModels.AgeOfEmpires;using DotNetCoreKata.Enums;namespace DotNetCoreKata.Services.AgeOfEmpires ;public class Client { public IUnit Train (UnitCategory unitCategory ) { Console.Write($"Ask resources to build unit: {unitCategory} ." ); return TrainingCenter.Create(unitCategory); } }
[單一職責原則 ] 我們將 創建類別 (Creator) 與 產品類別 (Product) 視作兩種不同的職責。
[開放封閉原則 ]
當新增新的產品 時,我們僅需新增產品類別 及修改創建類別 。
當修改既有產品 時,我們僅需修改其產品類別 及創建類別 , 不會影響到其他的產品類別。
無論是新增/修改產品 ,我們都不用再去修改到客戶端 (Client) 。
[依賴反轉原則 ]
運輸類別依賴抽象的機型介面。
產品類別實作抽象的機型介面。
透過簡單工廠模式 ,客戶端減少了改變的機會, 但創建類別依然常常需要變更。
ʕ •ᴥ•ʔ:重讀一次 Design Pattern,改用 C# 來寫範例。