1. 程式人生 > 實用技巧 >建立型模式之工廠方法

建立型模式之工廠方法

目錄

定義與特點

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

我們把被建立的物件稱為“產品”,把建立產品的物件稱為“工廠”。

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

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

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

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

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

結構與實現

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

模式的結構

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

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

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

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

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

模式的實現

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

//訪問類
class Program
{
    static void Main(string[] args)
    {
        //工廠方法
        Product product1 = new ConcreteFactory1().CreatProduct();
        product1.ShowInfo();
        Product product2 = new ConcreteFactory2().CreatProduct();
        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 CreatProduct();
}
/// <summary>
/// 具體工廠類1
/// </summary>
public class ConcreteFactory1 : AbstractFactory
{
    public override Product CreatProduct()
    {
        return new ConcreteProduct1();
    }
}
/// <summary>
/// 具體工廠類2
/// </summary>
public class ConcreteFactory2 : AbstractFactory
{
    public override Product CreatProduct()
    {
        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 電視工廠、海信電視工廠等。
  • 建立物件的任務由多個具體子工廠中的某一個完成,而抽象工廠只提供建立產品的介面。
  • 客戶不關心建立產品的細節,只關心產品的品牌。

擴充套件:簡單工廠模式

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