簡單工廠模式

一、模式概述

從設計模式的類型上來說,簡單工廠模式是屬於創建型模式,又叫做靜態工廠方法(Static Factory Method)模式,但不屬於23種GOF設計模式之一。

簡單工廠模式是由一個工廠對象決定創建出哪一種產品類的實例,是工廠模式家族中最簡單實用的模式,可以理解爲是不同工廠模式的一個特殊實現。

來分析一個現實生活中的案例,每天早晨起牀洗唰後是幹什麼呢?吃早餐、坐車、去公司上班。OK,下面分析下吃早餐中的故事,先看下面這個圖:


當我們在買早餐的時候,早餐店裏都賣什麼呢?這點你有注意嗎?衆多食品擺在那裏,你只對營業員說你要何種食品,他便會知道給你拿什麼樣的食品給你,這說明什麼呢?

如果用面向對象的思想來理解的話,營業員在這裏就充當了一個工廠的角色,他負責根據你的請求返回你需要的食品對象。而這一點正是簡單工廠模式的意圖。

二、模式意圖

簡單工廠模式根據提供給他的數據,返回幾個可能類中的一個類的實例。

三、模式UML圖

下面是簡單工廠模式的示意性UML圖:


如上圖,簡單工廠模式UML畫了兩種,詳細如下:

① 只有一個產品對象的簡單工廠模式。
② 帶有一個抽象產品對象的簡單工廠模式。

四、模式參與者

工廠(Factory)角色:接受客戶端的請求,通過請求負責創建相應的產品對象。
抽象產品(AbstractProduct)角色:是工廠模式所創建對象的父類或是共同擁有的接口。可是抽象類或接口。
具體產品(ConcreteProduct)對象:工廠模式所創建的對象都是這個角色的實例。

五、模式實現

通過上面的分析,已經清晰的知道了工廠模式中的各種角色和職責,那工廠模式通過代碼是怎麼實現的呢?OK,下面將繼續分析上面的吃早餐中的故事,做一個簡單的示例實現。

1、首先我們來看看只有一個產品對象的簡單工廠模式的實現。其實這很好理解,就當店裏只賣一種食品,這裏以饅頭爲例。

/// <summary>
/// 饅頭
/// </summary>
public class SteamedBread
{
    /// <summary>
    /// 構造方法
    /// </summary>
    public SteamedBread()
    { }

    /// <summary>
    /// 銷售價格
    /// </summary>
    private double price=0.5;
    public double Price
    {
        get { return price; }
        set { price = value; }
    }
}

產品對象建立好了,接下來是工廠對象:

/// <summary>
/// 工廠角色
/// </summary>
public class Factory
{
    /// <summary>
    /// 創建一個饅頭(SteamedBread)對象
    /// </summary>
    /// <returns></returns>
    public static SteamedBread CreateInstance()
    {
        return new SteamedBread();
    }
}

如上就完成了一個簡單工廠模式的簡單實現,一個產品和一個工廠,工廠負責創建這個產品。

但是這種實現有一定的缺陷,爲每一種產品創建一個靜態方法來完成產品對象的創建,如果有多個產品則需要定義多個靜態方法分別返回不同的對象,用設計原則來說的話這樣的實現不符合依賴倒置原則(DIP)。

如果系統裏只有一個單獨的產品對象,那麼採用這種實現是完全可以的。UML圖如下:


2、帶抽象產品(AbstractProduct)角色的簡單工廠模式實現

從上面分析中得知,這種實現得爲每一種產品創建一個靜態方法來完成產品對象的創建。雖然帶有簡單工廠的性質,但是又好象不能夠完全體現出簡單工廠模式的意圖。簡單工廠的意圖是:根據提供給他的數據,返回幾個可能類中的一個類的實例

根據意圖出發進行分析,要實現完全滿足簡單工廠模式意圖的程序,也就得根據提供給工廠的數據,讓工廠根據這個數據來進行判斷,然後返回相應的對象。OK,看看是下面這樣的嗎?

示意性代碼如下:

/// <summary>
/// 食品接口----扮演抽象產品角色
/// </summary>
public interface IFood
{
    /// <summary>
    /// 每種食品都有銷售價格,這裏應該作爲共性提升到父類或是接口來
    /// 由於我們只需要得到價格,所以這裏就只提供get屬性訪問器
    /// </summary>
    double price{get;}
}
//------------------------------------------------------------------------------------
/// <summary>
/// 饅頭
/// </summary>
public class SteamedBread:IFood
{
    /// <summary>
    /// 構造方法
    /// </summary>
    public SteamedBread()
    { }

    public double price
    {
        get
        {
            return  0.5;
        }
    }
}
//------------------------------------------------------------------------------------
/// <summary>
/// 包子
/// </summary>
public class SteamedStuffed:IFood
{
    public SteamedStuffed()
    { }

    /// <summary>
    /// 銷售價格
    /// </summary>
    public double price
    {
        get
        {
            return  0.6;  //0.6元一個
        }
    }
}
//------------------------------------------------------------------------------------
/// <summary>
/// 工廠角色
/// </summary>
public class Factory
{
    /// <summary>
    /// 創建一個饅頭(SteamedBread)對象
    /// </summary>
    /// <returns></returns>
    public static IFood CreateInstance(string key)
    {
        if (key == "饅頭")
        {
            return new SteamedBread();
        }
        else
        {
            return new SteamedStuffed();
        }
    }
}
//------------------------------------------------------------------------------------
public class Client
{
    public static void Main(string[] args)
    {
        //通過工廠創建一個產品的實例
        IFood food = Factory.CreateInstance("饅頭");
        Console.WriteLine("饅頭{0}元一個!", food.price);

        food = Factory.CreateInstance("包子");
        Console.WriteLine("包子{0}元一個!", food.price);
    }
}

此時的設計就已經完全符合簡單工廠模式的意圖了。顧客(Client)對早餐店營業員(Factory)說,我要“饅頭”,於是營業員便根據顧客所提供的數據(饅頭),去衆多食品中找,找到了然後就拿給顧客。

3、模式的演變實現

有些情況下Simple Factory可以由抽象產品角色扮演,一個抽象產品類同時是子類的工廠。也就是說,抽象產品角色扮演兩種角色和職責,出了基本的定義還還兼任工廠角色的職責,負責產品的創建工作。這裏我們在上面的例子基礎上適當修改一下OK了,新建立一個抽象類(Evolution):

/// <summary>
/// 兼任抽象產品角色和工廠角色兩種角色
/// </summary>
public abstract class Evolution
{
  /// <summary>
  /// 共性字段
  /// </summary>
  private double price;
  public double Price
  {
    get { return price; }
    set { price = value; }
  }

  public static Evolution CreateInstance(string key)
  {
    if (key == "饅頭")
    {
      return new SteamedBread();
    }
    else
    {
      return new SteamedStuffed();
    }
  }
}
public class SteamedBread : Evolution 
{ 
    public SteamedBread() 
    {
        this.Price = 0.5;  //在構造方法裏初始話屬性的值 
    } 
} 
 
public class SteamedStuffed : Evolution
{
    public SteamedStuffed()
    {
        this.Price = 0.6;
    }
}

通過上面的演化,此時客戶端的調用如下:

public class Client
{
    public static void Main(string[] args)
    {
        Evolution el = Evolution.CreateInstance("包子");
        Console.WriteLine("包子{0}元一個!", el.Price);
    }
}

UML草圖:
在實際的開發中,這種演化是很適用的,可以說是一種編程技巧吧。

4、模式的其他演變

如果系統中只有唯一的一個具體產品對象,那麼可以省略抽象產品角色。這一種其實也就是本錢前面的第一種模式實現。

如果抽象產品角色省略,那麼工廠角色就可以與具體產品角色合併。也就是說一個產品類就是自身的工廠。這樣把原有三個獨立的角色:抽象產品角色、具體產品角色和工廠角色合併爲一個,這個類自己負責創建自己的實例。

六、模式優缺點

工廠類含有必要的判斷邏輯,可以決定在什麼時候創建哪一個產品類的實例,客戶端可以免除直接創建產品對象的責任,而僅僅"消費"產品。簡單工廠模式通過這種做法實現了對責任的分割。
當產品有複雜的多層等級結構時,工廠類只有自己,以不變應萬變,就是模式的缺點。因爲工廠類集中了所有產品創建邏輯,一旦不能正常工作,整個系統都要受到影響。
系統擴展困難,一旦添加新產品就不得不修改工廠邏輯,有可能造成工廠邏輯過於複雜,違背了"開放--封閉"原則(OCP).另外,簡單工廠模式通常使用靜態工廠方法,這使得無法由子類繼承,造成工廠角色無法形成基於繼承的等級結構。

七、相關模式

工廠方法模式:每個產品由一個專門的工廠來負責創建。是一種只有唯一一個產品的實現,帶有簡單工廠的性質。
抽象工廠模式:大致和工廠方法相同。
單例模式:單例的實現和上面模式演變中的最後一種很相似,只要把構造器私有便OK。


八、參考資料

Addison-Wesley,1995,p.185. 中文版:《設計模式:可複用的面向對象軟件的基礎》 李英軍等譯.
Alan Sharroway & James r.Trott.中文版:《設計模式精解》
張逸 著《軟件設計精要與模式》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章