概述
工廠方法模式(FactoryMethod),定義一個建立產品物件的工廠介面,讓工廠子類決定例項化那一個產品類。我們把被建立的物件稱為“產品”,把建立產品的物件稱為“工廠”。如果要建立的產品不多,只要一個工廠類就可以完成,這種模式叫“簡單工廠模式”,它不屬於 23 種經典設計模式,它的缺點是增加新產品時會違背“開閉原則”(可以透過反射克服該缺點)。
工廠方法模式是對簡單工廠模式的進一步抽象化,其好處是可以使系統在不修改原來程式碼的情況下引進新的產品,即滿足開閉原則。
需求案例
我們先來看一看這樣一個案例,小學生玩的四則運算,當客戶輸入兩個數,然後輸入一個運運算元,獲得結果!我們先用簡單工廠寫下邏輯程式碼,驗證下簡單工廠的缺點。
簡單工廠程式碼
/// <summary> /// 靜態工作方法 實際上就是把建立物件的過程放到靜態方法裡面 /// </summary> public class CalFactory { public static ICalculator GetCalculator(string oper) { ICalculator? calculator = null; switch (oper) { case "*": calculator = new Mul(); break; case "+": calculator = new Add(); break; case "-": calculator = new Sub(); break; case "/": calculator = new Div(); break; } return calculator; } }
public interface ICalculator { double GetResult(double d1,double d2); } public class Add : ICalculator { public double GetResult(double d1, double d2) { return d1+ d2; } } public class Sub : ICalculator { public double GetResult(double d1, double d2) { return d1-d2; } } public class Mul : ICalculator { public double GetResult(double d1, double d2) { return d1* d2; } } public class Div : ICalculator { public double GetResult(double d1, double d2) { return d1 / d2; }
//C# 控制檯呼叫 Console.WriteLine("簡單工廠設計模式!"); Console.WriteLine("請輸入運算元1"); var d1 = Convert.ToDouble(Console.ReadLine()); Console.WriteLine("請輸入運算元2"); var d2 = Convert.ToDouble(Console.ReadLine()); ICalculator calculator = null; Console.WriteLine("請輸入運運算元"); string flag=Console.ReadLine(); calculator= CalFactory.GetCalculator(flag); double reslut= calculator.GetResult(d1,d2); Console.WriteLine(reslut);
這個需求的程式碼邏輯很簡單,一看基本就會,我們主要說下簡單工廠模式的優缺點;
優點:
1、簡單工廠設計模式解決了客戶端直接依賴具體物件的問題,客戶端消除了建立物件的責任,僅僅承擔使用的責任。簡單工廠模式實現了對責任的分割;
2、簡單工廠起到了程式碼複用的作用;
缺點:
1、系統擴充套件困難,一旦加入新功能,就必須要修改工廠邏輯。破壞了開閉原則;
2、簡單工廠集合了多有建立物件的邏輯,一旦不能正常工作,會導致整個系統出問題;
工廠方法程式碼
還是上面的需求,我們定義一個建立物件的介面,讓子類決定例項化哪一個類。
public interface ICalFactory { ICalculator GetCalculator(); } public class MulFactory : ICalFactory { public ICalculator GetCalculator() { return new Mul(); } } public class AddFactory : ICalFactory { public ICalculator GetCalculator() { return new Add(); } } public class SubFactory : ICalFactory { public ICalculator GetCalculator() { return new Sub(); } } public class DivFactory : ICalFactory { public ICalculator GetCalculator() { return new Div(); } }
public interface ICalculator { double GetResult(double d1, double d2); } public class Add : ICalculator { public double GetResult(double d1, double d2) { return d1 + d2; } } public class Sub : ICalculator { public double GetResult(double d1, double d2) { return d1 - d2; } } public class Mul : ICalculator { public double GetResult(double d1, double d2) { return d1 * d2; } } public class Div : ICalculator { public double GetResult(double d1, double d2) { return d1 / d2; } }
//C# 控制檯呼叫 Console.WriteLine("請輸入運算元1"); var d1 = Convert.ToDouble(Console.ReadLine()); Console.WriteLine("請輸入運算元2"); var d2 = Convert.ToDouble(Console.ReadLine()); Console.WriteLine("請輸入運運算元"); string flag = Console.ReadLine(); ICalFactory? calFactory = null; switch (flag) { case "*": calFactory = new MulFactory(); break; case "+": calFactory = new AddFactory(); break; case "-": calFactory = new SubFactory(); break; case "/": calFactory = new DivFactory(); break; } ICalculator calculator=calFactory?.GetCalculator(); var result= calculator.GetResult(d1, d2); Console.WriteLine(result);
我們目前是使用了工廠方法設計模式來實現了需求,但能看到原始碼裡面有一坨switch case,我們接下來透過反射處理switch case
沒有switch case的工廠方法程式碼
public class OperFactory:Attribute { public string Oper { get; } public OperFactory(string value) { Oper=value; } }
public class ReflectionFactory { //根據使用者輸入的運運算元 返回一個物件 Dictionary<string, ICalFactory> dic=new Dictionary<string, ICalFactory>(); public ReflectionFactory() { //拿到當前正在執行的程式集 Assembly assembly= Assembly.GetExecutingAssembly(); foreach (var item in assembly.GetTypes()) { //IsAssignableFrom 表示item繼承了ICalFactory或實現了ICalFactory if (typeof(ICalFactory).IsAssignableFrom(item)&&!item.IsInterface) { OperFactory of= item.GetCustomAttribute<OperFactory>(); if (of != null) { //給鍵值對集合賦值 dic[of.Oper] = Activator.CreateInstance(item) as ICalFactory; } } } } public ICalFactory GetFac(string s) { if (dic.ContainsKey(s)) { return dic[s]; } return null; } }
public interface ICalFactory { ICalculator GetCalculator(); } [OperFactory("*")] public class MulFactory : ICalFactory { public ICalculator GetCalculator() { return new Mul(); } } [OperFactory("+")] public class AddFactory : ICalFactory { public ICalculator GetCalculator() { return new Add(); } } [OperFactory("-")] public class SubFactory : ICalFactory { public ICalculator GetCalculator() { return new Sub(); } } [OperFactory("/")] public class DivFactory : ICalFactory { public ICalculator GetCalculator() { return new Div(); } }
//C# 控制檯呼叫 ReflectionFactory reflectionFactory = new ReflectionFactory(); ICalFactory calFactory2= reflectionFactory.GetFac(flag); var result2= calFactory2.GetCalculator().GetResult(d1, d2); Console.WriteLine(result2);
總結
一個四則運算的需求,分別採用簡單工廠和工廠方法實現,其實各有優缺點,使用哪種設計模式主要取決於自己的業務,有何疑問,歡迎交流。