[設計模式]工廠模式
本文主要論述了以下幾方面:
1.總論
2.簡單工廠模式
3.工廠方法模式
4.抽象工廠模式
1.總論
在工廠設計模式中,客戶端可以請求一個對象,而無需知道這個對象來自哪裏;也就是說,使用哪個類來生成這個對象。工廠背後的思想是簡化對象的創建。與客戶端自己基於類實例化直接創建對象相比,基於一個中心化函數來實現,更易於追蹤創建了哪些對象。通過將創建對象的代碼和使用對象的代碼解耦,工廠能夠降低應用維護的復雜度。
工廠通常有兩種形式:一種是工廠方法( Factory Method),它是一個方法,對不同的輸入參數返回不同的對象;第二種是抽象工廠,它是一組用於創建一系列相關事物對象的工廠方法。
- 簡單工廠模式
- 工廠方法
- 抽象工廠
這三種模式從上到下逐步抽象,並且更具一般性。
三者區別:
工廠方法模式:簡單工廠模式可視為工廠方法的一種特例。一個抽象產品類,可以派生出多個具體產品類。 一個抽象工廠類,可以派生出多個具體工廠類。 每個具體工廠類只能創建一個具體產品類的實例。
抽象工廠模式:多個抽象產品類,每個抽象產品類可以派生出多個具體產品類。 一個抽象工廠類,可以派生出多個具體工廠類。 每個具體工廠類可以創建多個具體產品類的實例。
總結:
工廠方法模式只有一個抽象產品類,而抽象工廠模式有多個。
2.簡單工廠模式
建立一個一個工廠來制造新的對象。它存在的目的很簡單:定義一個用於創建對象的接口。
按照面向過程的習慣,可能直接在客戶端中根據條件來創建不同的具體產品實例;對於簡單工廠模式而言,是把這部分邏輯抽象出來,放在一個工廠類中,由工廠類負責產生具體的對象,也就是將生產者和消費者分離了。
簡單工廠模式,通過專門定義一個類來負責創建具體類型的實例,要創建的實例一般繼承自同一個類;
結構一般如下所示:
如上所示,工廠模式中的角色一般包括:
工廠角色:即上圖中的OperationFactory,它可以被客戶端調用,其內部負責創建具體的對象;
class OperationFactory: # 定義一個工廠,根據用戶輸入來生產實例對象 operator = {} operator["+"] = OperationAdd() operator["-"] = OperationSub() operator["*"] = OperationMul() operator["/"] = OperationDiv() def createOperation(self, ch): if ch in self.operator: op = self.operator[ch] else: op = OperationUndef() return op
抽象產品類:即上圖中的抽象類Operation,它描述了所有實例公共的接口;
class Operation: # 基類,所有操作類都從此類繼承 def GetResult(self): pass
具體產品類:即上圖中的OperationAdd等,實現抽象產品的接口,是工廠角色中要創建的具體實例
class OperationAdd(Operation): # 加法類 def GetResult(self): return self.op1 + self.op2 class OperationSub(Operation): # 減法類 def GetResult(self): return self.op1 - self.op2 class OperationMul(Operation): # 乘法類 def GetResult(self): return self.op1 * self.op2 class OperationDiv(Operation): # 除法類 def GetResult(self): try: result = self.op1 / self.op2 return result except ZeroDivisionError as e: print("error:divided by zero.",e) return 0
主函數實現:
if __name__ == "__main__": op = input("operator: ") opa = int(input("a: ")) opb = int(input("b: ")) factory = OperationFactory() cal = factory.createOperation(op) cal.op1 = opa cal.op2 = opb print(cal.GetResult())主函數
簡單工廠優缺點總結:
優點:從上面的描述可以看出,工廠角色負責產生具體的實例對象,所以在工廠類中需要有必要的邏輯,通過客戶的輸入能夠得到具體創建的實例;所以客戶端就不需要感知具體對象是怎麽產生的,只需要將必要的信息提供給工廠即可;
缺點:簡單工廠模式是違反“開閉原則”,即對擴展開放,對修改關閉;因為如果要新增具體產品,就需要修改工廠類的代碼;
下面我們從開閉原則上來分析下簡單工廠模式。當客戶不再滿足現有功能的時候,想要一個開方操作,只要這種操作符合抽象產品制定的合同,那麽只要通知工廠類知道就可以被客戶使用了。所以對產品部分來說,它是符合開閉原則的;但是工廠部分好像不太理想,因為每增加一種操作,都要在工廠類(OperationFactory)中增加相應的創建業務邏輯(方法需要新增case來判斷創建什麽樣的操作),這顯然是違背開閉原則的。可想而知對於新產品的加入,工廠類是很被動的。對於這樣的工廠類,我們稱它為全能類 或者上帝類。
我們舉的例子是最簡單的情況,而在實際應用中,很可能產品是一個多層次的樹狀結構。由於簡單工廠模式中只有一個工廠類來對應這些產品,所以這可能會把我們的上帝累壞了,也累壞了我們這些程序員。於是工廠方法模式作為救世主出現了。 工廠類定義成了接口,而每新增的操作,就增加該種類型對應工廠類的實現,這樣工廠的設計就可以擴展了,而不必去修改原來的代碼。
3.工廠方法模式
工廠方法模式:定義一個用於創建對象的接口,讓子類決定實例化哪一個類。工廠方法讓類把實例化推遲到了子類。
對於簡單工廠而言,創建對象的邏輯判斷放在了工廠類中,客戶不感知具體的類,但是工廠類違反了開閉原則,如果要新增新的具體類,就必須修改工廠類;
對於工廠模式而言,是通過擴展來新增具體類的,但是在客戶端就必須感知到具體的類了,要通過具體類的創建工廠來創建具體的實例,也就是判斷邏輯由簡單工廠的工廠類挪到了客戶端中;
工廠模式橫向擴展很方便;
結構一般如下所示:
工廠方法模式組成:
1)抽象工廠角色: 這是工廠方法模式的核心,它與應用程序無關。是具體工廠角色必須實現的接口或者必須繼承的父類。
import abc class Creator(metaclass=abc.ABCMeta): """ Declare the factory method, which returns an object of type Product. Creator may also define a default implementation of the factory method that returns a default ConcreteProduct object. Call the factory method to create a Product object. """ def __init__(self): self.product = self._factory_method() @abc.abstractmethod def _factory_method(self): pass def some_operation(self): self.product.interface()抽象工廠
2)具體工廠角色:它含有和具體業務邏輯有關的代碼。由應用程序調用以創建對應的具體產品的對象。
class ConcreteCreator1(Creator): """ Override the factory method to return an instance of a ConcreteProduct1. """ def _factory_method(self): return ConcreteProduct1() class ConcreteCreator2(Creator): """ Override the factory method to return an instance of a ConcreteProduct2. """ def _factory_method(self): return ConcreteProduct2()具體工廠
3)抽象產品角色:它是具體產品繼承的父類或者是實現的接口。
import abc class Product(metaclass=abc.ABCMeta): """ Define the interface of objects the factory method creates. """ @abc.abstractmethod def interface(self): passView Code
4)具體產品角色:具體工廠角色所創建的對象就是此角色的實例。
class ConcreteProduct1(Product): """ Implement the Product interface. """ def interface(self): pass class ConcreteProduct2(Product): """ Implement the Product interface. """ def interface(self): pass具體產品
主函數實現:
def main(): concrete_creator = ConcreteCreator1() concrete_creator.product.interface() concrete_creator.some_operation() if __name__ == "__main__": main()主函數
這個和簡單工廠有區別,簡單工廠模式只有一個工廠,工廠方法模式對每一個產品都有相應的工廠。
工廠方法模式使用繼承自抽象工廠角色的多個子類來代替簡單工廠模式中的“上帝類”。正如上面所說,這樣便分擔了對象承受的壓力;而且這樣使得結構變得靈活起來——當有新的產品產生時,只要按照抽象產品角色、抽象工廠角色提供的合同來生成,那麽就可以被客戶使用,而不必去修改任何已有 的代碼。可以看出工廠角色的結構也是符合開閉原則的!
工廠方法模式優缺點總結:
優點:1.增加一個產品,只需要增加產品類和對應的工廠類,而不必修改總的工廠類。可以使代碼結構清晰,有效地封裝變化。
2.對調用者屏蔽具體的產品類。如果使用工廠模式,調用者只關心產品的接口就可以了,至於具體的實現,調用者根本無需關心。即使變更了具體的實現,對調用者來說沒有任何影響。
3.降低耦合度。產品類的實例化通常來說是很復雜的,它需要依賴很多的類,而這些類對於調用者來說根本無需知道,如果使用了工廠方法,我們需要做的僅僅是實例化好產品類,然後交給調用者使用。對調用者來說,產品所依賴的類都是透明的。
缺點:增加新的產品類,會修改客戶端代碼,工廠方法只是把簡單工廠的內部邏輯判斷移到了客戶端進行。簡單對象,特別是只需要通過new就可以完成創建的對象,無需使用工廠模式。如果使用工廠模式,就需要引入一個工廠類,會增加系統的復雜度。
可以看出工廠方法的加入,使得對象的數量成倍增長。當產品種類非常多時,會出現大量的與之對應的工廠對象,這不是我們所希望的。因為如果不能避免這種情 況,可以考慮使用簡單工廠模式與工廠方法模式相結合的方式來減少工廠類:即對於產品樹上類似的種類(一般是樹的葉子中互為兄弟的)使用簡單工廠模式來實 現。
工廠方法模式應用場景:
- 當客戶程序不需要知道要使用對象的創建過程。
- 客戶程序使用的對象存在變動的可能,或者根本就不知道使用哪一個具體的對象。
- 工廠模式是一種典型的解耦模式,迪米特法則在工廠模式中表現的尤為明顯。假如調用者自己組裝產品需要增加依賴關系時,可以考慮使用工廠模式。將會大大降低對象之間的耦合度。
- 當需要系統有比較好的擴展性時,可以考慮工廠模式,不同的產品用不同的實現工廠來組裝。
4.抽象工廠模式
抽象工廠設計模式是抽象方法的一種泛化。概括來說,一個抽象工廠是(邏輯上的)一組工廠方法,其中的每個工廠方法負責產生不同種類的對象。
可以說,抽象工廠模式和工廠方法模式的區別就在於需要創建對象的復雜程度上。而且抽象工廠模式是三個裏面最為抽象、最具一般性的。
抽象工廠模式的用意為:給客戶端提供一個接口,可以創建多個產品族中的產品對象 ,而且使用抽象工廠模式還要滿足一下條件:
- 系統中有多個產品族,而系統一次只可能消費其中一族產品。
- 同屬於同一個產品族的產品以其使用。
抽象工廠模式與工廠方法模式的區別:
抽象工廠模式是工廠方法模式的升級版本,他用來創建一組相關或者相互依賴的對象。他與工廠方法模式的區別就在於,工廠方法模式針對的是一個產品等級結構;而抽象工廠模式則是針對的多個產品等級結構。在編程中,通常一個產品結構,表現為一個接口或者抽象類,也就是說,工廠方法模式提供的所有產品都是衍生自同一個接口或抽象類,而抽象工廠模式所提供的產品則是衍生自不同的接口或抽象類。
在抽象工廠模式中,有一個產品族的概念:所謂的產品族,是指位於不同產品等級結構中功能相關聯的產品組成的家族。抽象工廠模式所提供的一系列產品就組成一個產品族;而工廠方法提供的一系列產品稱為一個等級結構。我們拿生產汽車的例子來說明他們之間的區別。
在上面的類圖中,兩廂車和三廂車稱為兩個不同的等級結構;而2.0排量車和2.4排量車則稱為兩個不同的產品族。再具體一點,2.0排量兩廂車和2.4排量兩廂車屬於同一個等級結構,2.0排量三廂車和2.4排量三廂車屬於另一個等級結構;而2.0排量兩廂車和2.0排量三廂車屬於同一個產品族,2.4排量兩廂車和2.4排量三廂車屬於另一個產品族。
明白了等級結構和產品族的概念,就理解工廠方法模式和抽象工廠模式的區別了,如果工廠的產品全部屬於同一個等級結構,則屬於工廠方法模式;如果工廠的產品來自多個等級結構,則屬於抽象工廠模式。在本例中,如果一個工廠模式提供2.0排量兩廂車和2.4排量兩廂車,那麽他屬於工廠方法模式;如果一個工廠模式是提供2.4排量兩廂車和2.4排量三廂車兩個產品,那麽這個工廠模式就是抽象工廠模式,因為他提供的產品是分屬兩個不同的等級結構。當然,如果一個工廠提供全部四種車型的產品,因為產品分屬兩個等級結構,他當然也屬於抽象工廠模式了。
結構一般如下所示:
抽象工廠模式的各個角色(和工廠方法一樣):
- 抽象工廠角色: 這是工廠方法模式的核心,它與應用程序無關。是具體工廠角色必須實現的接口或者必須繼承的父類。
import abc class AbstractFactory(metaclass=abc.ABCMeta): """ Declare an interface for operations that create abstract product objects. """ @abc.abstractmethod def create_product_a(self): pass @abc.abstractmethod def create_product_b(self): pass抽象工廠角色
- 具體工廠角色:它含有和具體業務邏輯有關的代碼。由應用程序調用以創建對應的具體產品的對象。
class ConcreteFactory1(AbstractFactory): """ Implement the operations to create concrete product objects. """ def create_product_a(self): return ConcreteProductA1() def create_product_b(self): return ConcreteProductB1() class ConcreteFactory2(AbstractFactory): """ Implement the operations to create concrete product objects. """ def create_product_a(self): return ConcreteProductA2() def create_product_b(self): return ConcreteProductB2()具體工廠角色
- 抽象產品角色:它是具體產品繼承的父類或者是實現的接口。
class AbstractProductA(metaclass=abc.ABCMeta): """ Declare an interface for a type of product object. """ @abc.abstractmethod def interface_a(self): pass抽象產品角色A 抽象產品角色B
- 具體產品角色:具體工廠角色所創建的對象就是此角色的實例。
class ConcreteProductA1(AbstractProductA): """ Define a product object to be created by the corresponding concrete factory. Implement the AbstractProduct interface. """ def interface_a(self): pass class ConcreteProductA2(AbstractProductA): """ Define a product object to be created by the corresponding concrete factory. Implement the AbstractProduct interface. """ def interface_a(self): pass具體產品角色A
class ConcreteProductB1(AbstractProductB): """ Define a product object to be created by the corresponding concrete factory. Implement the AbstractProduct interface. """ def interface_b(self): pass class ConcreteProductB2(AbstractProductB): """ Define a product object to be created by the corresponding concrete factory. Implement the AbstractProduct interface. """ def interface_b(self): pass具體產品角色B
主函數實現:
def main(): for factory in (ConcreteFactory1(), ConcreteFactory2()): product_a = factory.create_product_a() product_b = factory.create_product_b() product_a.interface_a() product_b.interface_b() if __name__ == "__main__": main()主函數
抽象工廠模式的優缺點:
優點: 抽象工廠模式除了具有工廠方法模式的優點外,最主要的優點就是可以在類的內部對產品族進行約束。所謂的產品族,一般或多或少的都存在一定的關聯,抽象工廠模式就可以在類內部對產品族的關聯關系進行定義和描述,而不必專門引入一個新的類來進行管理。
缺點:產品族的擴展將是一件十分費力的事情,假如產品族中需要增加一個新的產品,則幾乎所有的工廠類都需要進行修改。所以使用抽象工廠模式時,對產品等級結構的劃分是非常重要的。
適用場景:
當需要創建的對象是一系列相互關聯或相互依賴的產品族時,便可以使用抽象工廠模式。說的更明白一點,就是一個繼承體系中,如果存在著多個等級結構(即存在著多個抽象類),並且分屬各個等級結構中的實現類之間存在著一定的關聯或者約束,就可以使用抽象工廠模式。假如各個等級結構中的實現類之間不存在關聯或約束,則使用多個獨立的工廠來對產品進行創建,則更合適一點。
總結,無論是簡單工廠模式,工廠方法模式,還是抽象工廠模式,他們都屬於工廠模式,在形式和特點上也是極為相似的,他們的最終目的都是為了解耦。在使用時,我們不必去在意這個模式到底工廠方法模式還是抽象工廠模式,因為他們之間的演變常常是令人琢磨不透的。經常你會發現,明明使用的工廠方法模式,當新需求來臨,稍加修改,加入了一個新方法後,由於類中的產品構成了不同等級結構中的產品族,它就變成抽象工廠模式了;而對於抽象工廠模式,當減少一個方法使的提供的產品不再構成產品族之後,它就演變成了工廠方法模式。
所以,在使用工廠模式時,只需要關心降低耦合度的目的是否達到了。
[設計模式]工廠模式