簡單工廠
首先,我們先看一個簡單工廠的例子:
#coding=utf-8
class Mercedes(object):
"""梅賽德斯
"""
def __repr__(self):
return "Mercedes-Benz"
class BMW(object):
"""寶馬
"""
def __repr__(self):
return "BMW"
假設我們有兩個“産品”分别是Mercedes和BMW的汽車,如果沒有“工廠”來生産它們,我們就要在代碼中自己進行執行個體化,如:
mercedes = Mercedes()
bmw = BMW()
但現實中,你可能會面對很多汽車産品,而且每個産品的構造參數還不一樣,這樣在建立執行個體時會遇到麻煩。這時就可以構造一個“簡單工廠”把所有汽車執行個體化的過程封裝在裡面。
class SimpleCarFactory(object):
"""簡單工廠
"""
@staticmethod
def product_car(name):
if name == 'mb':
return Mercedes()
elif name == 'bmw':
return BMW()
有了SimpleCarFactory類後,就可以通過向固定的接口傳入參數獲得想要的對象執行個體,如下:
c1 = SimpleCarFactory.product_car('mb')
c2 = SimpleCarFactory.product_car('bmw')
工廠方法
雖然有了一個簡單的工廠,但在實際使用工廠的過程中,我們會發現新問題:如果我們要新增一個“産品”,例如Audi的汽車,我們除了新增一個Audi類外還要修改SimpleCarFactory内的product_car方法。這樣就違背了軟體設計中的開閉原則,即在擴充新的類時,盡量不要修改原有代碼。是以我們在簡單工廠的基礎上把SimpleCarFactory抽象成不同的工廠,每個工廠對應生成自己的産品,這就是工廠方法
#coding=utf-8
import abc
class AbstractFactory(object):
"""抽象工廠
"""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def product_car(self):
pass
class MercedesFactory(AbstractFactory):
"""梅賽德斯工廠
"""
def product_car(self):
return Mercedes()
class BMWFactory(AbstractFactory):
"""寶馬工廠
"""
def product_car(self):
return BMW()
"""
我們把工廠抽象出來用abc子產品 實作了一個抽象的基類AbstractFactory,
這樣就可以通過特定的工廠來獲得特定的産品執行個體了:
"""
c1 = MercedesFactory().product_car()
c2 = BMWFactory().product_car()
每個工廠負責生産自己的産品也避免了我們在新增産品時需要修改工廠的代碼,而隻要增加相應的工廠即可。如新增一個Audi産品,隻需新增一個Audi類和AudiFactory類。
抽象工廠
工廠方法雖然解決了我們“修改代碼”的問題,但如果我們要生産很多産品,就會發現我們同樣需要寫很多對應的工廠類。比如如果MercedesFactory和BMWFactory不僅生産小汽車,還要生産SUV,那我們用工廠方法就要再多構造兩個生産SUV的工廠類。是以為了解決這個問題,我們就要再更進一步的抽象工廠類,讓一個工廠可以生産同一類的多個産品,這就是抽象工廠。具體實作如下
#coding=utf-8
import abc
# 兩種小汽車
class Mercedes_C63(object):
"""梅賽德斯 C63
"""
def __repr__(self):
return "Mercedes-Benz: C63"
class BMW_M3(object):
"""寶馬 M3
"""
def __repr__(self):
return "BMW: M3"
# 兩種SUV
class Mercedes_G63(object):
"""梅賽德斯 G63
"""
def __repr__(self):
return "Mercedes-Benz: G63"
class BMW_X5(object):
"""寶馬 X5
"""
def __repr__(self):
return "BMW: X5"
class AbstractFactory(object):
"""抽象工廠
可以生産小汽車外,還可以生産SUV
"""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def product_car(self):
pass
@abc.abstractmethod
def product_suv(self):
pass
class MercedesFactory(AbstractFactory):
"""梅賽德斯工廠
"""
def product_car(self):
return Mercedes_C63()
def product_suv(self):
return Mercedes_G63()
class BMWFactory(AbstractFactory):
"""寶馬工廠
"""
def product_car(self):
return BMW_M3()
def product_suv(self):
return BMW_X5()
我們讓基類AbstractFactory同時可以生産汽車和SUV,然後令MercedesFactory和BMWFactory繼承AbstractFactory并重寫product_car和product_suv方法即可。
c1 = MercedesFactory().product_car()
s1 = MercedesFactory().product_suv()
print(c1, s1)
s2 = BMWFactory().product_suv()
c2 = BMWFactory().product_car()
print(c2, s2)
抽象工廠模式與工廠方法模式最大的差別在于,抽象工廠中的一個工廠對象可以負責多個不同産品對象的建立 ,這樣比工廠方法模式更為簡單、有效率
結論
初學設計模式時會對三種工廠模式的實際應用比較困惑,其實三種模式各有優缺點,應用的場景也不盡相同:
- 簡單工廠模式适用于需建立的對象較少,不會造成工廠方法中的業務邏輯太過複雜的情況下,而且使用者隻關心那種類型的執行個體被建立,并不關心其初始化過程時,比如多種資料庫(MySQL/MongoDB)的執行個體,多種格式檔案的解析器(XML/JSON)等。
- 工廠方法模式繼承了簡單工廠模式的優點又有所改進,其不再通過一個工廠類來負責所有産品的建立,而是将具體建立工作交給相應的子類去做,這使得工廠方法模式可以允許系統能夠更高效的擴充。實際應用中可以用來實作系統的日志系統等,比如具體的程式運作日志,網絡日志,資料庫日志等都可以用具體的工廠類來建立。
- 抽象工廠模式在工廠方法基礎上擴充了工廠對多個産品建立的支援,更适合一些大型系統,比如系統中有多于一個的産品族,且這些産品族類的産品需實作同樣的接口,像很多軟體系統界面中不同主題下不同的按鈕、文本框、字型等等。