寫給準備跳槽的小夥伴們的設計模式——工廠方法設計模式

2023-02-15 09:00:36

概述

工廠方法模式(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);

總結

一個四則運算的需求,分別採用簡單工廠和工廠方法實現,其實各有優缺點,使用哪種設計模式主要取決於自己的業務,有何疑問,歡迎交流。