Python 設計模式系列之二: 創建型 Simple Factory 模式

一、創建型模式

創建型模式(Creational Pattern)對類的實例化過程進行了抽象,能夠使軟件模塊做到與對象創建和組織的無關性。爲了使體系結構更加清晰,一些軟件在設計上要求當創建類的具體實例時,能夠根據具體的語境來動態地決定怎樣創建對象,創建哪些對象,以及怎樣組織和表示這些對象,而創建型模式所要描述的就是該如何來解決這些問題。

按照生成目標的不同,創建型模式可以分爲類的創建型模式和對象的創建型模式兩種:

  • 類的創建型模式
    類的創建型模式通過使用繼承關係,將類的創建交由具體的子類來完成,這樣就向外界隱藏瞭如何得到具體類的實現細節,以及這些類的實例是如何被創建和組織在一起的。
  • 對象的創建型模式
    對象的創建型模式通過把對象的創建委託給另一個對象來完成,可以根據語境動態地決定生成哪些具體類的實例,同時還可以向外界隱藏這些實例是如何被創建以及如何被組織在一起的細節。

所有的創建型模式都有兩個永恆的主旋律:第一,它們都將系統使用哪些具體類的信息封裝起來;第二,它們隱藏了這些類的實例是如何被創建和組織的。外界對於這些對象只知道它們共同的接口,而不清楚其具體的實現細節。正因如此,創建型模式在創建什麼(what),由誰(who)來創建,以及何時(when)創建這些方面,都爲軟件設計者提供了儘可能大的靈活性。

具體到Python來說,假設有這樣一個類:

class Person:
    def __init__(self, name):
        self.name = name

要創建該類的一個實例,則應該執行下面的語句:

p = Person("Gary")

但如果創建對象時完成的工作非常複雜,需要一段很長的代碼,你就不能簡單地將其全部寫入__init__方法中,因爲這會違背面向對象思想的兩個基本原則:封裝(encapsulation)和委派(delegation)。假如執意要做樣做,結果只會使你的代碼變成一段行爲固定的硬編碼(hard coding),而整個軟件的結構都極有可能變得非常糟糕,因爲其它某個模塊也許就正依賴於你所創建的這個實例,這樣就在無形之間增加了模塊之間的耦合度。

將Python對象的創建過程封裝到某個類中來單獨完成,可以使你的程序變得更加靈活和通用。實踐證明,使用下面的六種創建型模式可以更好地改善對象的創建過程:

  • Simple Factory模式
      專門定義一個類來負責創建其它類的實例,被創建的實例通常都具有共同的父類。
  • Factory Method模式
      將對象的創建交由父類中定義的一個標準方法來完成,而不是其構造函數,究竟應該創建何種對象由具體的子類負責決定。
  • Abstract Factory模式
      提供一個共同的接口來創建相互關聯的多個對象。
  • Singleton模式
      保證系統只會產生該類的一個實例,同時還負責向外界提供訪問該實例的標準方法。
  • Builder模式
      將複雜對象的創建同它們的具體表現形式(representation)區別開來,這樣可以根據需要得到具有不同表現形式的對象。
  • Prototype模式
      利用一個能對自身進行復制的類,使得對象的動態創建變得更加容易。

二、模式引入

簡單工廠(Simple Factory)模式又稱爲靜態工廠方法(Static Factory Method)模式,屬於類的創建型模式。這種模式根據外界給定的信息,由"工廠"對象"製造"出某些可能"產品"類中的一個實例,工廠對象能夠處理的所有類通常都繼承於同一個父類,並且對外界提供基本相同的接口,只不過在具體實現時會有所差別罷了。

假設我們要開發一個繪圖程序,用來繪製簡單的幾何圖形,這個軟件應該能夠處理下面的幾種幾何對象:

  • 圓形(Circle)
  • 矩形(Rectangle)
  • 菱形(Diamond)

除了各自特有的屬性和方法之外,所有的幾何圖形幾乎都可以抽象出繪製(draw)和擦除(erase)兩個公共方法,因而可以爲它們定義一個共同的接口Shape。雖然Python語言本身並不支持接口,但爲了更好地闡明設計模式的思想,有時我們還是會借用一下UML中的接口這一概念。這樣一來,各個類之間的關係就將如圖1所示:



圖1

Shape接口定義了所有幾何圖形都必須實現的公共方法: draw()和erase(),實現該接口的Python代碼如下所示,Python中沒有接口的概念,因此在具體實現時可以使用類來替代。

代碼清單1:shape.py
class Shape:
    # 繪製圖形
    def draw(self):
        pass
    # 擦除圖形
    def erase(self):
        pass

Circle類是Shape的一種具體形式,它實現了Shape接口定義的所有方法,此外還添加了一個屬性__radius,用來表示圓的半徑。以下是實現Circle類的代碼:

代碼清單2:circle.py
class Circle (Shape):
    def __init__(self, radius = 0):
        self.__radius = radius
    # 繪製圓形
    def draw(self):
        print "Draw Circle"
    # 擦除圓形
    def erase(self):
        print "Erase Circle"
    # 半徑的取值方法
    def getRadius(self):
        return self.__radius
    # 半徑的賦值方法
    def setRadius(self, radius):
        self.__radius = radius

Rectangle類也是Shape的一種具體形式,它實現了Shape接口定義的所有方法,並添加了__width和__height兩個屬性,分別表示矩形的寬度和高度。以下是實現Rectangle類的代碼:

代碼清單3:rectangle.py
class Rectangle (Shape):
    def __init__(self, width = 0, height = 0):
        self.__width = width
        self.__height = height
    # 繪製矩形
    def draw(self):
        print "Draw Rectangle"
    # 擦除矩形
    def erase(self):
        print "Erase Rectangle"
    # 寬度的取值方法
    def getWidth(self):
        return self.__width
    # 寬度的賦值方法
    def setWidth(self, width):
        self.__width = width
    # 高度的取值方法
    def getHeight(self):
        return self.__height
    # 高度的賦值方法
    def setHeight(self, height):
        self.__height = height

同樣,Diamond類也是Shape的一種具體形式,它實現了Shape接口中定義的所有方法,並且添加了__width和__height兩個屬性,分別表示菱形的寬度和高度。以下是實現Diamond類的代碼:

代碼清單4:diamond.py
class Diamond (Shape):
    def __init__(self, width = 0, height = 0):
        self.__width = width
        self.__height = height
    # 繪製菱形
    def draw(self):
        print "Draw Diamond"
    # 擦除菱形
    def erase(self):
        print "Erase Diamond"
    # 寬度的取值方法
    def getWidth(self):
        return self.__width
    # 寬度的賦值方法
    def setWidth(self, width):
        self.__width = width
    # 高度的取值方法
    def getHeight(self):
        return self.__height
    # 高度的賦值方法
    def setHeight(self, height):
        self.__height = height

所有幾何圖形類都定義好後,下面要做的就是提供一個"工廠"類ShapeFactory,來創建各種幾何圖形的具體實例。ShapeFactory類的作用就是根據外界的要求,創建出不同的幾何圖形對象,如圓形(Circle)、矩形(Rectangle)或菱形(Diamond),這樣整個軟件的體系結構就將如圖2所示。



圖2

ShapeFactory類用於創建各種幾何圖形的實例,其實現代碼如下所示:

代碼清單5:shapefactory.py
class ShapeFactory:
    def factory(self, which):
        if which == "Circle":
            return Circle()
        elif which == "Rectangle":
            return Rectangle()
        elif which == "Diamond":
            return Diamond()
        else:
            return None

在ShapeFactory類中只定義了一個方法factory(),外界通過調用該方法,來創建其所需的幾何圖形對象,但如果所請求的類是系統所不支持的,則將返回None。在引入了工廠類之後,其它模塊如果想生成幾何圖形類的實例,只需調用ShapeFactory類的factory()方法就可以了:

fac = ShapeFactory()
shape = fac.factory("Diamond")
if shape != None:
    shape.draw()

就樣就成功地將類是如何創建的這一實現細節向外界隱藏起來了,這就是簡單工廠模式所採取的基本策略。

三、一般結構

簡單工廠模式屬於類的創建型模式,適合用來對大量具有共同接口的類進行實例化,它可以推遲到運行的時候才動態決定要創建哪個類的實例,而不是在編譯時就必須知道要實例化哪個類。簡單工廠模式的一般性結構如圖3所示:



圖3

簡單工廠模式的實質是由一個工廠類根據傳入的參量,動態決定應該創建出哪一個產品類的實例。從上圖可以看出,簡單工廠模式涉及到工廠角色、抽象產品角色和具體產品角色三個參與者。

  • 工廠(Creator)角色
    是簡單工廠模式的核心,它負責實現創建所有實例的內部邏輯。工廠類可以被外界直接調用,創建所需的產品對象。
  • 抽象產品(Product)角色
    是簡單工廠模式所創建的所有對象的父類,它負責描述所有實例所共有的公共接口。
  • 具體產品(Concrete Product)角色
    是簡單工廠模式的創建目標,所有創建的對象都是充當這個角色的某個具體類的實例。

工廠角色負責創建一個具體產品的實例,並將其返回給調用者,以下是工廠類的示例性Python代碼:

代碼清單6:creator.py
class Creator:
    # 創建具體產品類的方法
    def factory(self):
        return ConcreteProduct()

抽象產品角色的主要目的是爲所有的具體產品提供一個共同的接口,通常只需給出相應的聲明就可以了,而不用給出具體的實現。以下是抽象產品類的示例性Python代碼:

代碼清單7:product.py
class Product:
    # 所有具體產品類的公共接口
    def interface(self):
        pass

具體產品角色充當最終的創建目標,一般來講它是抽象產品類的子類,實現了抽象產品類中定義的所有接口方法。以下是具體產品類的示例性Python代碼:

代碼清單8:concrete.py
class ConcreteProduct(Product):
    # 公共接口的實現
    def interface(self):
        print "Concrete Product Method"

在應用簡單工廠模式時,可以採用下面的示例性Python代碼:

fac = Creator()
p = fac.factory()
if p != None:
    p.interface()

在這個簡單的示意性實現裏,充當具體產品角色的類只有一個,但在真正的實際應用中,通常遇到的都是同時有多個具體產品類的情況。

四、實際運用

在運用簡單工廠模式開發真正的軟件系統時,抽象產品和具體產品之間可能會形成非常複雜的樹狀結構,如圖4所示。



圖4

雖然這樣的結構並不符合所有具體產品類都繼承於同一抽象產品類的基本原則,但也不難發現其實所有的具體產品類歸根結底都源自於同一個抽象產口類,遇到這種情況時只需稍加變通,依然能夠使用同一個工廠類來創建所有具體產品類的實例,只不過此時的軟件結構將如圖5所示。



圖5

當抽象產品類和具體產品類的關係變得複雜起來時,使用簡單工廠模式的一個好處就是各種產品類之間的結構關係不會反映到工廠類中,並且它們結構上的變化也不會影響到工廠類。正是因爲簡單工廠模式對各種具體產品類者是單獨對待的,因此工廠類並不需要弄清楚它們之間的結構關係,而只需知道究竟有哪些具體產品類可以被實例化以及如何實例化就可以了。不過這樣做有時也會產生一些麻煩,那就是在增加新的具體產品類時,不可避免地會導致對工廠類的修改。

如果在實際中運用簡單工廠模式時遇到只有一個具體產品類的情況,爲了簡化軟件體系結構,可以考慮對簡單工廠模式進行精簡,省略抽象產品這一角色。此時簡單工廠模式的結構將如圖6所示。



圖6

在簡單廠模式中,充當工廠角色的類通常只會有一個方法,那就是用來創建具體產品對象的factory()。在某些特殊場合下,如果需要的話也可以考慮將工廠角色的功能交由抽象產品角色來替代完成,這時簡單工廠模式的結構將如圖7所示。



圖7

在更極端的情況下,工廠角色、抽象產品角色和具體產品角色三者可以合併,即將所有的功能都交由具體產品類來統一完成。也就是說,簡單工廠模式將退化成一個單獨的具體產品類,這個類同時也是自身的工廠,負責創建自己的實例,如圖8所示。



圖8

這種退化的簡單工廠模式從實質上講等價於一個類,唯一不同的是在對象初始化時不再使用默認的構造方法,而是使用自定義的工廠方法,但這在許多場合下的意義並不是很大,尤其是對Python這種動態類型語言來說更是如此。由於退化後的模式並沒有充分利用簡單工廠模式的特點,所以最好慎重使用,當然也可以考慮用其它模式進行替代。

五、優勢和不足

在簡單工廠模式中,工廠類是整個模式的關鍵所在,它包含必要的判斷邏輯,能夠根據外界給定的信息,決定究竟應該創建哪個具體類的對象。通過使用工廠類,外界可以從直接創建具體產品對象的尷尬局面中擺脫出來,僅僅只需要負責"消費"對象就可以了,而不必管這些對象究竟是如何創建以及如何組織的,這樣就清晰了各自的職責和權力,有利於整個軟件體系結構的優化。

不過,凡事有利就有弊,簡單工廠模式的缺點也正體現在其工廠類上。由於工廠類集中了所有實例的創建邏輯,是一個無所不能的"全能類"(God Class),因此它對整個軟件系統是至關重要的,如果這個類無法正常工作,其它部分可能都會受到牽連,正所謂"城門失火,殃及池魚"。

將全部創建邏輯都集中到一個工廠類中還有另外一個缺點,那就是當系統中的具體產品類不斷增多時,可能會出現要求工廠類根據不同條件創建不同實例的需求,這種對條件的判斷和對具體產品類型的判斷交織在一起,很難避免模塊功能的蔓延,對系統的擴展和維護也非常不利。

簡單工廠模式的這些缺點,在下次將要介紹的工廠方法(Factory Method)模式中得到了一定程度上的克服,好了,下次再見吧!

六、小結

創建型模式的目標是做到對象創建和組織的無關性,它可以細分爲類的創建型模式和對象的創建型模式。簡單工廠模式屬於類的創建型模式,它可以根據外界所給定的信息,用工廠對象製造出某種特定產品類的實例。完整的簡單工廠模式包含工廠、抽象產品和具體產品三個角色,但在實際運用時可以靈活掌握,對模型進行簡單化。簡單工廠模式的優點是它的工廠類能夠負責所有對象的創建,而缺點則是工廠類的實現邏輯可能過於複雜。

轉自:http://www.ibm.com/developerworks/cn/linux/l-pypt/part2/index.html

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