創建型模式之工廠方法


定義與特點

工廠方法(FactoryMethod)模式的定義:定義一個創建產品對象的工廠接口,將產品對象的實際創建工作推遲到具體子工廠類當中。這滿足創建型模式中所要求的“創建與使用相分離”的特點。

我們把被創建的對象稱為“產品”,把創建產品的對象稱為“工廠”。

如果要創建的產品不多,只要一個工廠類就可以完成,這種模式叫“簡單工廠模式”,它不屬於 GoF 的 23 種經典設計模式,它的缺點是增加新產品時會違背“開閉原則”(可以通過反射克服該缺點)

“工廠方法模式”是對簡單工廠模式的進一步抽象化,其好處是可以使系統在不修改原來代碼的情況下引進新的產品,即滿足開閉原則。

工廠方法模式的主要優點有:

  • 用戶只需要知道具體工廠的名稱就可得到所要的產品,無須知道產品的具體創建過程(對創建過程復雜的對象很有作用)
  • 在系統增加新的產品時只需要添加具體產品類和對應的具體工廠類,無須對原工廠進行任何修改,滿足開閉原則;

其缺點是:每增加一個產品就要增加一個具體產品類和一個對應的具體工廠類,這增加了系統的復雜度

結構與實現

工廠方法模式由抽象工廠、具體工廠、抽象產品和具體產品等4個要素構成。本節來分析其基本結構和實現方法。

模式的結構

工廠方法模式的主要角色如下:

  • 抽象工廠(Abstract Factory):提供了創建產品的接口,調用者通過它訪問具體工廠的工廠方法 CreateProduct() 來創建產品。
  • 具體工廠(ConcreteFactory):主要是實現抽象工廠中的抽象方法,完成具體產品的創建
  • 抽象產品(Product):定義了產品的規范,描述了產品的主要特性和功能。
  • 具體產品(ConcreteProduct):實現了抽象產品角色所定義的接口,由具體工廠來創建,它同具體工廠之間一一對應。

抽象工廠、抽象產品可以是接口,也可以是抽象類。抽象產品的定義方式取決於產品對象的建模事物,抽象工廠的定義方式一般與抽象產品的定義方式保持一致。

一般情況下,產品對象是對現實世界中的事物進行建模,使用抽象類定義抽象產品更合理。特殊情況下,產品對象對功能組件、功能實現建模或產品對象已有父對象(C#中對象只能有一個父類)時則使用接口定義抽象產品。

其結構圖如圖所示(使用抽象類):

模式的實現

根據上圖寫出的該模式的代碼如下:

//訪問類
class Program
{
    static void Main(string[] args)
    {
        //工廠方法
        Product product1 = new ConcreteFactory1().CreateProduct();
        product1.ShowInfo();
        Product product2 = new ConcreteFactory2().CreateProduct();
        product2.ShowInfo();        
        Console.ReadKey();
    }
}


/// <summary>
/// 抽象產品類
/// </summary>
public abstract class Product
{
    public abstract void ShowInfo();
}
/// <summary>
/// 具體產品類1
/// </summary>
public class ConcreteProduct1 : Product
{
    public override void ShowInfo()
    {
        Console.WriteLine("產品類型為Product1");
    }
}
/// <summary>
/// 具體產品類2
/// </summary>
public class ConcreteProduct2 : Product
{
    public override void ShowInfo()
    {
        Console.WriteLine("產品類型為Product2");
    }
}

/// <summary>
/// 抽象工廠類
/// </summary>
public abstract class AbstractFactory
{
    public abstract Product CreateProduct();
}
/// <summary>
/// 具體工廠類1
/// </summary>
public class ConcreteFactory1 : AbstractFactory
{
    public override Product CreateProduct()
    {
        return new ConcreteProduct1();
    }
}
/// <summary>
/// 具體工廠類2
/// </summary>
public class ConcreteFactory2 : AbstractFactory
{
    public override Product CreateProduct()
    {
        return new ConcreteProduct2();
    }
}

運行結果:

產品類型為Product1
產品類型為Product2

上述代碼,添加新類型的產品,需要新增新產品類和新產品工廠類,工廠父類AbstractFactory不需要修改,這樣對於已有的工廠不會產生任何潛在的改動影響——便於擴展。
這種方式和直接new ConcreteProduct1()、new ConcreteProduct2()有點像,只不過由每一種Product自己的工廠類復雜new操作,主要原因如下:

  • new一個Product出來,可能有很多字段都需要賦值,還要運行一些初始化方法。如果這些賦值、方法在客戶client類里面寫,那么其他所有客戶都要寫這些代碼,代碼重復。如果這些賦值、方法寫在產品類的構造函數里面,要干的事情特別多,幾十行上百行的代碼,全放構造函數里面,有點違背設計原則,在構造函數做太復雜的操作,當出錯時發現錯誤有時會很困難。構造函數應該盡量簡單,一眼望穿,最好能保證永不出現構造失敗的情況。
  • 如果ConcreteProduct1多了一個子類ConcreteProduct1Child,現在生產產品時返回這個類的對象,那么所有客戶端的new ConcreteProduct1,都要換成new ConcreteProduct1Child,而如果做成工廠模式,直接在返回ConcreteProduct1的代碼哪里更改一下返回ConcreteProduct1Child類的對象就可以了,反正返回的都是Product1(抽象父類)——方便維護
  • 如果所有的產品類,在返回對象之前,又要添加新的邏輯了,那么需要一個個類的找,修改,如果全集中在工廠類中,直接在工廠類中修改即可,避免遺漏,還是——方便維護

如果返回一個實例要做很多事情,不好直接寫在構造函數里面,可以寫在具體的工廠類里面,而且如果工廠父類要添加新的邏輯,所有工廠同時享有。所以工廠模式主要是有代碼重用,分割職責解耦,便於維護的特點。

應用場景

工廠方法模式通常適用於以下場景:

  • 客戶只知道創建產品的工廠名,而不知道具體的產品名。如 TCL 電視工廠、海信電視工廠等。
  • 創建對象的任務由多個具體子工廠中的某一個完成,而抽象工廠只提供創建產品的接口。
  • 客戶不關心創建產品的細節,只關心產品的品牌。

擴展:簡單工廠模式

當需要生成的產品不多且不會增加,一個具體工廠類就可以完成任務時,可刪除抽象工廠類。這時工廠方法模式將退化到簡單工廠模式,其結構圖如圖所示:

在上面的實現代碼上增加簡單工廠方法的實現,代碼如下:

//簡單工廠方法
public class SimpleFactory
{
    public static Product CreateProduct(string name)
    {
        Product product = null;
        if (name == "ConcreteProduct1")
        {
            product = new ConcreteProduct1();
        }
        else if (name == "ConcreteProduct2")
        {
            product = new ConcreteProduct2();
        }
        return product;
    }
}

//簡單工廠方法(加反射優化)
public class ReflectionSimpleFactory
{

    public static Product CreateProduct(string name)
    {
        //獲取當前程序集
        Assembly ass = Assembly.GetCallingAssembly();
        //解析程序集名稱
        AssemblyName assName = new AssemblyName(ass.FullName);
        //獲取程序集的類型
        Type t = ass.GetType(assName.Name+"."+name);
        //創建類的實例對象
        Product o = (Product)Activator.CreateInstance(t);
        return o;
    }
}

參考文章

C#工廠模式——簡單工廠、工廠方法、反射+簡單工廠、抽象工廠——CSDN


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM