用Python實(shí)現(xiàn)設(shè)計(jì)模式——工廠模式

前言

工廠模式嫩挤,顧名思義就是我們可以通過一個(gè)指定的“工廠”獲得需要的“產(chǎn)品”肾请,在設(shè)計(jì)模式中主要用于抽象對(duì)象的創(chuàng)建過程练慕,讓用戶可以指定自己想要的對(duì)象而不必關(guān)心對(duì)象的實(shí)例化過程枉阵。這樣做的好處是用戶只需通過固定的接口而不是直接去調(diào)用類的實(shí)例化方法來獲得一個(gè)對(duì)象的實(shí)例译红,隱藏了實(shí)例創(chuàng)建過程的復(fù)雜度,解耦了生產(chǎn)實(shí)例和使用實(shí)例的代碼兴溜,降低了維護(hù)的復(fù)雜性侦厚。
本文會(huì)用Python實(shí)現(xiàn)三種工廠模式的簡(jiǎn)單例子弥虐,所有代碼都托管在Github上嫁盲。

簡(jiǎn)單工廠

首先鞍匾,我們先看一個(gè)簡(jiǎn)單工廠的例子:

#coding=utf-8
class Mercedes(object):
    """梅賽德斯
    """
    def __repr__(self):
        return "Mercedes-Benz"

class BMW(object):
    """寶馬
    """
    def __repr__(self):
        return "BMW"

假設(shè)我們有兩個(gè)“產(chǎn)品”分別是MercedesBMW的汽車屑咳,如果沒有“工廠”來生產(chǎn)它們顾患,我們就要在代碼中自己進(jìn)行實(shí)例化外傅,如:

mercedes = Mercedes()
bmw = BMW()

但現(xiàn)實(shí)中薄声,你可能會(huì)面對(duì)很多汽車產(chǎn)品锡垄,而且每個(gè)產(chǎn)品的構(gòu)造參數(shù)還不一樣,這樣在創(chuàng)建實(shí)例時(shí)會(huì)遇到麻煩来破。這時(shí)就可以構(gòu)造一個(gè)“簡(jiǎn)單工廠”把所有汽車實(shí)例化的過程封裝在里面裁眯。

class SimpleCarFactory(object):
    """簡(jiǎn)單工廠
    """
    @staticmethod
    def product_car(name):
        if name == 'mb':
            return Mercedes()
        elif name == 'bmw':
            return BMW()

有了SimpleCarFactory類后,就可以通過向固定的接口傳入?yún)?shù)獲得想要的對(duì)象實(shí)例讳癌,如下:

c1 = SimpleCarFactory.product_car('mb')
c2 = SimpleCarFactory.product_car('bmw')

工廠方法

雖然有了一個(gè)簡(jiǎn)單的工廠穿稳,但在實(shí)際使用工廠的過程中,我們會(huì)發(fā)現(xiàn)新問題:如果我們要新增一個(gè)“產(chǎn)品”晌坤,例如Audi的汽車逢艘,我們除了新增一個(gè)Audi類外還要修改SimpleCarFactory內(nèi)的product_car方法。這樣就違背了軟件設(shè)計(jì)中的開閉原則[1]骤菠,即在擴(kuò)展新的類時(shí)它改,盡量不要修改原有代碼。所以我們?cè)诤?jiǎn)單工廠的基礎(chǔ)上把SimpleCarFactory抽象成不同的工廠商乎,每個(gè)工廠對(duì)應(yīng)生成自己的產(chǎn)品央拖,這就是工廠方法。

#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模塊[2]實(shí)現(xiàn)了一個(gè)抽象的基類AbstractFactory鹉戚,這樣就可以通過特定的工廠來獲得特定的產(chǎn)品實(shí)例了:

c1 = MercedesFactory().product_car()
c2 = BMWFactory().product_car()

每個(gè)工廠負(fù)責(zé)生產(chǎn)自己的產(chǎn)品也避免了我們?cè)谛略霎a(chǎn)品時(shí)需要修改工廠的代碼鲜戒,而只要增加相應(yīng)的工廠即可。如新增一個(gè)Audi產(chǎn)品抹凳,只需新增一個(gè)Audi類和AudiFactory類遏餐。

抽象工廠

工廠方法雖然解決了我們“修改代碼”的問題,但如果我們要生產(chǎn)很多產(chǎn)品赢底,就會(huì)發(fā)現(xiàn)我們同樣需要寫很多對(duì)應(yīng)的工廠類失都。比如如果MercedesFactoryBMWFactory不僅生產(chǎn)小汽車,還要生產(chǎn)SUV幸冻,那我們用工廠方法就要再多構(gòu)造兩個(gè)生產(chǎn)SUV的工廠類粹庞。所以為了解決這個(gè)問題,我們就要再更進(jìn)一步的抽象工廠類洽损,讓一個(gè)工廠可以生產(chǎn)同一類的多個(gè)產(chǎn)品庞溜,這就是抽象工廠。具體實(shí)現(xiàn)如下:

#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):
    """抽象工廠
    可以生產(chǎn)小汽車外趁啸,還可以生產(chǎn)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同時(shí)可以生產(chǎn)汽車和SUV强缘,然后令MercedesFactoryBMWFactory繼承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)

抽象工廠模式與工廠方法模式最大的區(qū)別在于不傅,抽象工廠中的一個(gè)工廠對(duì)象可以負(fù)責(zé)多個(gè)不同產(chǎn)品對(duì)象的創(chuàng)建 旅掂,這樣比工廠方法模式更為簡(jiǎn)單、有效率访娶。

結(jié)論

初學(xué)設(shè)計(jì)模式時(shí)會(huì)對(duì)三種工廠模式的實(shí)際應(yīng)用比較困惑商虐,其實(shí)三種模式各有優(yōu)缺點(diǎn),應(yīng)用的場(chǎng)景也不盡相同:

  • 簡(jiǎn)單工廠模式適用于需創(chuàng)建的對(duì)象較少,不會(huì)造成工廠方法中的業(yè)務(wù)邏輯太過復(fù)雜的情況下秘车,而且用戶只關(guān)心那種類型的實(shí)例被創(chuàng)建典勇,并不關(guān)心其初始化過程時(shí),比如多種數(shù)據(jù)庫(MySQL/MongoDB)的實(shí)例叮趴,多種格式文件的解析器(XML/JSON)等割笙。
  • 工廠方法模式繼承了簡(jiǎn)單工廠模式的優(yōu)點(diǎn)又有所改進(jìn),其不再通過一個(gè)工廠類來負(fù)責(zé)所有產(chǎn)品的創(chuàng)建眯亦,而是將具體創(chuàng)建工作交給相應(yīng)的子類去做伤溉,這使得工廠方法模式可以允許系統(tǒng)能夠更高效的擴(kuò)展。實(shí)際應(yīng)用中可以用來實(shí)現(xiàn)系統(tǒng)的日志系統(tǒng)等妻率,比如具體的程序運(yùn)行日志乱顾,網(wǎng)絡(luò)日志,數(shù)據(jù)庫日志等都可以用具體的工廠類來創(chuàng)建宫静。
  • 抽象工廠模式在工廠方法基礎(chǔ)上擴(kuò)展了工廠對(duì)多個(gè)產(chǎn)品創(chuàng)建的支持走净,更適合一些大型系統(tǒng),比如系統(tǒng)中有多于一個(gè)的產(chǎn)品族孤里,且這些產(chǎn)品族類的產(chǎn)品需實(shí)現(xiàn)同樣的接口伏伯,像很多軟件系統(tǒng)界面中不同主題下不同的按鈕、文本框扭粱、字體等等舵鳞。

參考

[1]維基百科
[2]Python官方文檔


2018/1/30更新:修改工廠方法的代碼示例,新增結(jié)論一節(jié)琢蛤。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市抛虏,隨后出現(xiàn)的幾起案子博其,更是在濱河造成了極大的恐慌,老刑警劉巖迂猴,帶你破解...
    沈念sama閱讀 212,080評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件慕淡,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡沸毁,警方通過查閱死者的電腦和手機(jī)峰髓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,422評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來息尺,“玉大人携兵,你說我怎么就攤上這事÷в” “怎么了徐紧?”我有些...
    開封第一講書人閱讀 157,630評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我并级,道長(zhǎng)拂檩,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,554評(píng)論 1 284
  • 正文 為了忘掉前任嘲碧,我火速辦了婚禮稻励,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘愈涩。我一直安慰自己钉迷,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,662評(píng)論 6 386
  • 文/花漫 我一把揭開白布钠署。 她就那樣靜靜地躺著糠聪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪谐鼎。 梳的紋絲不亂的頭發(fā)上舰蟆,一...
    開封第一講書人閱讀 49,856評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音狸棍,去河邊找鬼身害。 笑死,一個(gè)胖子當(dāng)著我的面吹牛草戈,可吹牛的內(nèi)容都是我干的塌鸯。 我是一名探鬼主播,決...
    沈念sama閱讀 39,014評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼唐片,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼丙猬!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起费韭,我...
    開封第一講書人閱讀 37,752評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤茧球,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后星持,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體抢埋,經(jīng)...
    沈念sama閱讀 44,212評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,541評(píng)論 2 327
  • 正文 我和宋清朗相戀三年督暂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了揪垄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,687評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡逻翁,死狀恐怖饥努,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情卢未,我是刑警寧澤肪凛,帶...
    沈念sama閱讀 34,347評(píng)論 4 331
  • 正文 年R本政府宣布堰汉,位于F島的核電站,受9級(jí)特大地震影響伟墙,放射性物質(zhì)發(fā)生泄漏翘鸭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,973評(píng)論 3 315
  • 文/蒙蒙 一戳葵、第九天 我趴在偏房一處隱蔽的房頂上張望就乓。 院中可真熱鬧,春花似錦拱烁、人聲如沸生蚁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,777評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽邦投。三九已至,卻和暖如春擅笔,著一層夾襖步出監(jiān)牢的瞬間志衣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,006評(píng)論 1 266
  • 我被黑心中介騙來泰國打工猛们, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留念脯,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,406評(píng)論 2 360
  • 正文 我出身青樓弯淘,卻偏偏與公主長(zhǎng)得像绿店,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子庐橙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,576評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容