轉載 用Python實現設計模式——工廠模式
轉載自 SegmentFault作者 夏秋 , https://segmentfault.com/a/1190000013053013
非常感謝這位作者的深入淺出的講解.
前言
工廠模式,顧名思義就是我們可以通過一個指定的“工廠”獲得需要的“產品”,在設計模式中主要用於抽象物件的建立過程,讓使用者可以指定自己想要的物件而不必關心物件的例項化過程。這樣做的好處是使用者只需通過固定的介面而不是直接去呼叫類的例項化方法來獲得一個物件的例項,隱藏了例項建立過程的複雜度,解耦了生產例項和使用例項的程式碼,降低了維護的複雜性。
本文會用Python實現三種工廠模式的簡單例子,所有程式碼都託管在Github 上。
簡單工廠
首先,我們先看一個簡單工廠的例子:
1 #coding=utf-8 2 class Mercedes(object): 3"""梅賽德斯 4""" 5def __repr__(self): 6return "Mercedes-Benz" 7 8 class BMW(object): 9"""寶馬 10""" 11def __repr__(self): 12return "BMW"
假設我們有兩個“產品”分別是Mercedes 和BMW 的汽車,如果沒有“工廠”來生產它們,我們就要在程式碼中自己進行例項化,如:
1 mercedes = Mercedes() 2 bmw = BMW()
但現實中,你可能會面對很多汽車產品,而且每個產品的構造引數還不一樣,這樣在建立例項時會遇到麻煩。這時就可以構造一個“簡單工廠”把所有汽車例項化的過程封裝在裡面。
1 class SimpleCarFactory(object): 2"""簡單工廠 3""" 4@staticmethod 5def product_car(name): 6if name == 'mb': 7return Mercedes() 8elif name == 'bmw': 9return BMW()
有了SimpleCarFactory 類後,就可以通過向固定的介面傳入引數獲得想要的物件例項,如下:
1 c1 = SimpleCarFactory.product_car('mb') 2 c2 = SimpleCarFactory.product_car('bmw')
工廠方法
雖然有了一個簡單的工廠,但在實際使用工廠的過程中,我們會發現新問題:如果我們要新增一個“產品”,例如Audi的汽車,我們除了新增一個Audi 類外還要修改SimpleCarFactory 內的product_car方法。這樣就違背了軟體設計中的開閉原則 [1],即在擴充套件新的類時,儘量不要修改原有程式碼。所以我們在簡單工廠的基礎上把SimpleCarFactory 抽象成不同的工廠,每個工廠對應生成自己的產品,這就是工廠方法。
1 #coding=utf-8 2 import abc 3 4 class AbstractFactory(object): 5"""抽象工廠 6""" 7__metaclass__ = abc.ABCMeta 8 [email protected] 10def product_car(self): 11pass 12 13 class MercedesFactory(AbstractFactory): 14"""梅賽德斯工廠 15""" 16def product_car(self): 17return Mercedes() 18 19 class BMWFactory(AbstractFactory): 20"""寶馬工廠 21""" 22def product_car(self): 23return BMW()
我們把工廠抽象出來用abc模組 實現了一個抽象的基類AbstractFactory ,這樣就可以通過特定的工廠來獲得特定的產品例項了:
1 c1 = MercedesFactory().product_car() 2 c2 = BMWFactory().product_car()
每個工廠負責生產自己的產品也避免了我們在新增產品時需要修改工廠的程式碼,而只要增加相應的工廠即可。如新增一個Audi產品,只需新增一個Audi 類和AudiFactory 類。
抽象工廠
工廠方法雖然解決了我們“修改程式碼”的問題,但如果我們要生產很多產品,就會發現我們同樣需要寫很多對應的工廠類。比如如果MercedesFactory 和BMWFactory 不僅生產小汽車,還要生產SUV,那我們用工廠方法就要再多構造兩個生產SUV的工廠類。所以為了解決這個問題,我們就要再更進一步的抽象工廠類,讓一個工廠可以生產同一類的多個產品,這就是抽象工廠。具體實現如下:
1 #coding=utf-8 2 import abc 3 4 # 兩種小汽車 5 class Mercedes_C63(object): 6"""梅賽德斯 C63 7""" 8def __repr__(self): 9return "Mercedes-Benz: C63" 10 11 class BMW_M3(object): 12"""寶馬 M3 13""" 14def __repr__(self): 15return "BMW: M3" 16 17 # 兩種SUV 18 class Mercedes_G63(object): 19"""梅賽德斯 G63 20""" 21def __repr__(self): 22return "Mercedes-Benz: G63" 23 24 class BMW_X5(object): 25"""寶馬 X5 26""" 27def __repr__(self): 28return "BMW: X5" 29 30 class AbstractFactory(object): 31"""抽象工廠 32可以生產小汽車外,還可以生產SUV 33""" 34__metaclass__ = abc.ABCMeta 35 [email protected] 37def product_car(self): 38pass 39 [email protected] 41def product_suv(self): 42pass 43 44 class MercedesFactory(AbstractFactory): 45"""梅賽德斯工廠 46""" 47def product_car(self): 48return Mercedes_C63() 49 50def product_suv(self): 51return Mercedes_G63() 52 53 class BMWFactory(AbstractFactory): 54"""寶馬工廠 55""" 56def product_car(self): 57return BMW_M3() 58 59def product_suv(self): 60return BMW_X5()
我們讓基類AbstractFactory 同時可以生產汽車和SUV,然後令MercedesFactory 和BMWFactory 繼承AbstractFactory 並重寫product_car和product_suv方法即可。
1 c1 = MercedesFactory().product_car() 2 s1 = MercedesFactory().product_suv() 3 print(c1, s1) 4 s2 = BMWFactory().product_suv() 5 c2 = BMWFactory().product_car() 6 print(c2, s2)
抽象工廠模式與工廠方法模式最大的區別在於,抽象工廠中的一個工廠物件可以負責多個不同產品物件的建立 ,這樣比工廠方法模式更為簡單、有效率。
結論
初學設計模式時會對三種工廠模式的實際應用比較困惑,其實三種模式各有優缺點,應用的場景也不盡相同:
- 簡單工廠模式適用於需建立的物件較少,不會造成工廠方法中的業務邏輯太過複雜的情況下,而且使用者只關心那種型別的例項被建立,並不關心其初始化過程時,比如多種資料庫(MySQL/MongoDB)的例項,多種格式檔案的解析器(XML/JSON)等。
- 工廠方法模式繼承了簡單工廠模式的優點又有所改進,其不再通過一個工廠類來負責所有產品的建立,而是將具體建立工作交給相應的子類去做,這使得工廠方法模式可以允許系統能夠更高效的擴充套件。實際應用中可以用來實現系統的日誌系統等,比如具體的程式執行日誌,網路日誌,資料庫日誌等都可以用具體的工廠類來建立。
- 抽象工廠模式在工廠方法基礎上擴充套件了工廠對多個產品建立的支援,更適合一些大型系統,比如系統中有多於一個的產品族,且這些產品族類的產品需實現同樣的介面,像很多軟體系統介面中不同主題下不同的按鈕、文字框、字型等等。