設(shè)計(jì)模式---工廠模式

1仰猖、工廠模式

所謂工廠模式芬探,就是作為一個(gè)工廠的類有一個(gè)對(duì)象以及與它關(guān)聯(lián)的多個(gè)方法,客戶端使用某些參數(shù)調(diào)用此方法厘惦,之后偷仿,工廠會(huì)據(jù)此創(chuàng)建所需類型的對(duì)象哩簿,然后將他們返回給客戶端。

客戶端也可以創(chuàng)建對(duì)象酝静,那為什么需要工廠的存在呢节榜?

因?yàn)楣S擁有以下優(yōu)點(diǎn):

  • 松耦合,即對(duì)象的創(chuàng)建可以獨(dú)立于類的實(shí)現(xiàn)
  • 對(duì)客戶端十分友好别智,只需知道傳遞的接口宗苍,方法和參數(shù),就可以創(chuàng)建所需的對(duì)象了
  • 可以輕松的在工廠中添加其他類來(lái)創(chuàng)建其他類型的對(duì)象薄榛,而無(wú)需更改客戶端代碼讳窟。
  • 工廠還可以重用現(xiàn)有對(duì)象。

下面介紹工廠模式的三個(gè)變體:

  • 簡(jiǎn)單工廠模式:不符合開(kāi)閉原則敞恋,不屬于23種設(shè)計(jì)模式之一
  • 工廠方法模式
  • 抽象工廠模式

2丽啡、簡(jiǎn)單工廠模式

簡(jiǎn)單工廠模式(Simple Factory Pattern):又稱為靜態(tài)工廠方法(Static Factory Method)模式,它屬于類創(chuàng)建型模式硬猫。在簡(jiǎn)單工廠模式中补箍,可以根據(jù)參數(shù)的不同返回不同類的實(shí)例。簡(jiǎn)單工廠模式專門(mén)定義一個(gè)類來(lái)負(fù)責(zé)創(chuàng)建其他類的實(shí)例啸蜜,被創(chuàng)建的實(shí)例通常都具有共同的父類坑雅。

from abc import ABCMeta, abstractclassmethod

class Animal(metaclass=ABCMeta):

    @abstractclassmethod  # 抽象類方法
    def do_say(self):
        pass

class Dog(Animal):
    def do_say(self):
        print('www')

class Cat(Animal):
    def do_say(self):
        print('mmm')

class ForestFactory:
    def make_sound(self, object_type):
        print('object_type的類型:',type(eval(object_type)))
        return eval(object_type)().do_say()

if __name__ == '__main__':
    ff = ForestFactory()
    animal = input('輸入哪種動(dòng)物來(lái)叫?')
    ff.make_sound(animal)
輸入哪種動(dòng)物來(lái)叫衬横?Cat
object_type的類型: <class 'abc.ABCMeta'>
mmm

如上裹粤,創(chuàng)建一個(gè)名為Animal的抽象產(chǎn)品。Animal是一個(gè)抽象的基類(ABCMeta是Python的特殊元類冕香,用來(lái)生成類Abstract)蛹尝,它帶有方法do_say()。我們利用Animal接口創(chuàng)建了兩種產(chǎn)品(Cat和Dog)悉尾,并實(shí)現(xiàn)了do_say()方法來(lái)提供這些動(dòng)物的相應(yīng)的叫聲突那。之后又為了給客戶端提供接口,創(chuàng)建了ForectFactory類构眯,在類中寫(xiě)了一個(gè)方法make_sound()愕难,用戶就可以將參數(shù)(Cat或Dog)傳給該方法,就能實(shí)現(xiàn)相應(yīng)的功能惫霸。

在看python實(shí)現(xiàn)代碼時(shí)遇到不少知識(shí)盲區(qū)猫缭,參考了下面幾篇博客:
Python內(nèi)置函數(shù)(19)——eval
Python裝飾器、metaclass壹店、abc模塊學(xué)習(xí)筆記
Python抽象類(abc模塊)

簡(jiǎn)單工廠模式的優(yōu)缺點(diǎn)

  • 優(yōu)點(diǎn)
    工廠類含有必要的判斷邏輯猜丹,可以決定在什么時(shí)候創(chuàng)建哪一個(gè)產(chǎn)品類的實(shí)例,客戶端可以免除直接創(chuàng)建產(chǎn)品對(duì)象的責(zé)任硅卢,而僅僅“消費(fèi)”產(chǎn)品射窒;簡(jiǎn)單工廠模式通過(guò)這種做法實(shí)現(xiàn)了對(duì)責(zé)任的分割藏杖,它提供了專門(mén)的工廠類用于創(chuàng)建對(duì)象。
    客戶端無(wú)須知道所創(chuàng)建的具體產(chǎn)品類的類名脉顿,只需要知道具體產(chǎn)品類所對(duì)應(yīng)的參數(shù)即可蝌麸,對(duì)于一些復(fù)雜的類名,通過(guò)簡(jiǎn)單工廠模式可以減少使用者的記憶量艾疟。
    通過(guò)引入配置文件来吩,可以在不修改任何客戶端代碼的情況下更換和增加新的具體產(chǎn)品類,在一定程度上提高了系統(tǒng)的靈活性蔽莱。

  • 缺點(diǎn)
    由于工廠類集中了所有產(chǎn)品創(chuàng)建邏輯弟疆,一旦不能正常工作,整個(gè)系統(tǒng)都要受到影響碾褂。
    使用簡(jiǎn)單工廠模式將會(huì)增加系統(tǒng)中類的個(gè)數(shù)兽间,在一定程序上增加了系統(tǒng)的復(fù)雜度和理解難度
    系統(tǒng)擴(kuò)展困難正塌,一旦添加新產(chǎn)品就不得不修改工廠邏輯嘀略,在產(chǎn)品類型較多時(shí),有可能造成工廠邏輯過(guò)于復(fù)雜乓诽,不利于系統(tǒng)的擴(kuò)展和維護(hù)帜羊。
    簡(jiǎn)單工廠模式由于使用了靜態(tài)工廠方法,造成工廠角色無(wú)法形成基于繼承的等級(jí)結(jié)構(gòu)鸠天。
    不符合開(kāi)閉原則讼育,當(dāng)需要?jiǎng)?chuàng)建新產(chǎn)品時(shí),需要修改其他已創(chuàng)建的類稠集。


工廠方法模式

在資料的搜尋過(guò)程中看到一個(gè)例子非常形象奶段,很容易理解,語(yǔ)法也沒(méi)上面的花里胡哨剥纷,雖然可能欠缺了點(diǎn)嚴(yán)謹(jǐn)性痹籍。

class LeiFeng():
    def buy_rice(self):
        pass

    def sweep(self):
        pass

class Student(LeiFeng):
    def buy_rice(self):
        print ('大學(xué)生幫你買米')

    def sweep(self):
        print ('大學(xué)生幫你掃地')


class Volunteer(LeiFeng):
    def buy_rice(self):
        print ('社區(qū)志愿者幫你買米')

    def sweep(self):
        print ('社區(qū)志愿者幫你掃地')


class LeiFengFactory():
    def create_lei_feng(self):
        pass

class StudentFactory(LeiFengFactory):
    def create_lei_feng(self):
        return Student()

class VolunteerFactory(LeiFengFactory):
    def create_lei_feng(self):
        return Volunteer()


if __name__ == '__main__':
    myFactory = StudentFactory()
    leifeng1 = myFactory.create_lei_feng()

    leifeng1.buy_rice()
    leifeng1.sweep()

解釋:它先創(chuàng)建了一個(gè)LeiFeng類,它可以買米晦鞋,掃地兩件事蹲缠,接下來(lái)由大學(xué)生和社區(qū)志愿者來(lái)繼承這個(gè)雷鋒精神,也有了這兩個(gè)優(yōu)點(diǎn)悠垛,之后线定,又創(chuàng)建了LeiFengFactory類,來(lái)培養(yǎng)(生產(chǎn))大學(xué)生和社區(qū)志愿者确买,讓大學(xué)生( StudentFactory)和社區(qū)志愿者(VolunteerFactory)繼承這個(gè)培養(yǎng)方案(LeiFengFactory)斤讥,在主函數(shù)中就可以通過(guò)調(diào)用大學(xué)生(StudentFactory)或社區(qū)志愿者(VolunteerFactory)類來(lái)創(chuàng)建實(shí)例,讓他們?nèi)プ龊檬拢?/p>

這個(gè)工廠方法模式相比于簡(jiǎn)單工廠方法的不同之處在于當(dāng)需要?jiǎng)?chuàng)建新的方法時(shí)湾趾,比如需要讓中學(xué)生也加入進(jìn)來(lái)周偎,那么就只需在新建兩個(gè)類即可抹剩,無(wú)需更改別的類撑帖,符合開(kāi)閉原則蓉坎。

class LeiFeng():
    def buy_rice(self):
        pass

    def sweep(self):
        pass

class Student(LeiFeng):
    def buy_rice(self):
        print ('大學(xué)生幫你買米')

    def sweep(self):
        print ('大學(xué)生幫你掃地')


class Volunteer(LeiFeng):
    def buy_rice(self):
        print ('社區(qū)志愿者幫你買米')

    def sweep(self):
        print ('社區(qū)志愿者幫你掃地')

# ------------新加的類---------------
class MiddleStudent(LeiFeng):
    def buy_rice(self):
        print('中學(xué)生幫你買米')
    def sweep(self):
        print ('中學(xué)生幫你掃地')
# ----------------------------------

class LeiFengFactory():
    def create_lei_feng(self):
        pass


class StudentFactory(LeiFengFactory):
    def create_lei_feng(self):
        return Student()


class VolunteerFactory(LeiFengFactory):
    def create_lei_feng(self):
        return Volunteer()

# ------------新加的類-----------------
class MiddleStudentFactory(LeiFengFactory):
    def create_lei_feng(self):
        return MiddleStudent()
# ----------------------------------

if __name__ == '__main__':
    myFactory = StudentFactory()
    leifeng1 = myFactory.create_lei_feng()

    leifeng1.buy_rice()
    leifeng1.sweep()

抽象工廠模式

通過(guò)工廠方法模式我們可以看到,當(dāng)各個(gè)方法一多胡嘿,如果為每個(gè)方法都創(chuàng)建一個(gè)類來(lái)對(duì)應(yīng)蛉艾,將會(huì)變得很復(fù)雜,所以引入了抽象的概念衷敌。工廠方法用于創(chuàng)建一個(gè)產(chǎn)品勿侯,那抽象工廠方法就是創(chuàng)建相關(guān)產(chǎn)品的系列,比如航空飛機(jī)缴罗,由多個(gè)工廠不同的零部件助琐,在運(yùn)輸?shù)浇M裝工廠組裝…………

下面以制作游戲?yàn)槔?/p>

在編寫(xiě)一款面向全年齡的游戲,游戲本身需要使用工廠方法進(jìn)行開(kāi)發(fā)面氓。但游戲也需要考慮不同年齡段玩家的需求和口味不同兵钮,所以需要為不同年齡段的玩家針對(duì)游戲進(jìn)行一定的修改。于是在用戶輸入年齡后舌界,運(yùn)行符合其年齡的要求的游戲掘譬。

class Frog:

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

    def __str__(self):
        return self.name

    def interact_with(self, obstacle):
        print('{} the Frog encounters {} and {}!'.format(self,
        obstacle, obstacle.action()))


class Bug:

    def __str__(self):
        return 'a bug'

    def action(self):
        return 'eats it'


class FrogWorld:

    def __init__(self, name):
        print(self)
        self.player_name = name

    def __str__(self):
        return '\n\n\t------ Frog World -------'

    def make_character(self):
        return Frog(self.player_name)

    def make_obstacle(self):
        return Bug()


class Wizard:

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

    def __str__(self):
        return self.name


    def interact_with(self, obstacle):
        print(
            '{} the Wizard battles against {} and {}!'.format(
            self,
            obstacle,
            obstacle.action()))


class Ork:

    def __str__(self):
        return 'an evil ork'

    def action(self):
        return 'kills it'


class WizardWorld:

    def __init__(self, name):
        print(self)
        self.player_name = name

    def __str__(self):
        return '\n\n\t------ Wizard World -------'

    def make_character(self):
        return Wizard(self.player_name)

    def make_obstacle(self):
        return Ork()

class GameEnvironment:

    def __init__(self, factory):
        self.hero = factory.make_character()
        self.obstacle = factory.make_obstacle()

    def play(self):
        self.hero.interact_with(self.obstacle)

def validate_age(name):
    try:
        age = input('Welcome {}. How old are you? '.format(name))
        age = int(age)
    except ValueError as err:
        print("Age {} is invalid, please try again...".format(err))
        return (False, err)
    return (True, age)

if __name__ == '__main__':
    name = input("Hello. What's your name? ")
    valid_input = False
    while not valid_input:
        valid_input, age = validate_age(name)
        game = FrogWorld if age < 18 else WizardWorld
        environment = GameEnvironment(game(name))
        environment.play()
Hello. What's your name? wayne
Welcome wayne. How old are you? 12


    ------ Frog World -------
wayne the Frog encounters a bug and eats it!

解釋:
1、通過(guò)一個(gè) GameEnvironment 去管理兩個(gè)游戲:FrogWorld呻拌、WizerdWorld 葱轩,根據(jù)用戶輸入的不同的年齡去決定其去玩那個(gè)游戲,是青蛙世界還是巫師世界呢藐握?
2靴拱、而每個(gè)游戲類即 FrogWorld、WizerdWorld 猾普,又分別管理著 兩個(gè)角色 --(Frog青蛙袜炕、Bug臭蟲(chóng))和(Wizerd巫師、Ork怪獸)
通過(guò)這種層層繼承的方式抬闷,將各個(gè)模塊管理好妇蛀,最終根據(jù)客戶端的輸入,來(lái)返回不同的游戲世界笤成。

其實(shí)無(wú)論是工廠模式和抽象工廠模式的思想都是一樣的评架,根據(jù)不同的輸入,調(diào)用相同的接口炕泳,得出不同的結(jié)果纵诞。其內(nèi)部封裝了操作流程,用戶無(wú)需知道其內(nèi)部如何其實(shí)現(xiàn)的如何進(jìn)行選擇培遵,只管輸入便可的得到結(jié)果浙芙。
參考自

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末登刺,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子嗡呼,更是在濱河造成了極大的恐慌纸俭,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件南窗,死亡現(xiàn)場(chǎng)離奇詭異揍很,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)万伤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)窒悔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人敌买,你說(shuō)我怎么就攤上這事简珠。” “怎么了虹钮?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵聋庵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我芜抒,道長(zhǎng)珍策,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任宅倒,我火速辦了婚禮攘宙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘拐迁。我一直安慰自己蹭劈,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布线召。 她就那樣靜靜地躺著铺韧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缓淹。 梳的紋絲不亂的頭發(fā)上哈打,一...
    開(kāi)封第一講書(shū)人閱讀 49,772評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音讯壶,去河邊找鬼料仗。 笑死,一個(gè)胖子當(dāng)著我的面吹牛伏蚊,可吹牛的內(nèi)容都是我干的立轧。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼氛改!你這毒婦竟也來(lái)了帐萎?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤胜卤,失蹤者是張志新(化名)和其女友劉穎疆导,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體瑰艘,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡是鬼,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了紫新。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡李剖,死狀恐怖芒率,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情篙顺,我是刑警寧澤偶芍,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站德玫,受9級(jí)特大地震影響匪蟀,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜宰僧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一材彪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧琴儿,春花似錦段化、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至晒屎,卻和暖如春喘蟆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鼓鲁。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工蕴轨, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人坐桩。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓尺棋,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子膘螟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

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

  • 設(shè)計(jì)模式——工廠模式 工廠模式核心是封裝對(duì)象的創(chuàng)建接口成福,將new對(duì)象的操作封裝起來(lái),方便創(chuàng)建使用荆残、管理對(duì)象奴艾。 工廠...
    Ant_way閱讀 538評(píng)論 0 0
  • 設(shè)計(jì)模式1 設(shè)計(jì)模式2 工廠模式 工廠模式可簡(jiǎn)單的分為三類:簡(jiǎn)單工廠,工廠方法内斯,抽象工廠 簡(jiǎn)單工廠模式 定義 簡(jiǎn)單...
    edison0428閱讀 230評(píng)論 0 0
  • 前言 最近在復(fù)習(xí)java設(shè)計(jì)模式中的工廠模式蕴潦。本來(lái)有一點(diǎn)小小的理解。感覺(jué)都寫(xiě)的不錯(cuò)俘闯,就是有點(diǎn)太零散了潭苞,最后還是決定...
    斌林誠(chéng)上閱讀 18,737評(píng)論 3 25
  • 一、工廠模式簡(jiǎn)介 意圖定義一個(gè)創(chuàng)建對(duì)象的接口真朗,讓其子類自己決定實(shí)例化哪一個(gè)工廠類此疹,工廠模式使其創(chuàng)建過(guò)程延遲到子類進(jìn)...
    怡紅快綠閱讀 596評(píng)論 0 0
  • 設(shè)計(jì)模式匯總 一、基礎(chǔ)知識(shí) 1. 設(shè)計(jì)模式概述 定義:設(shè)計(jì)模式(Design Pattern)是一套被反復(fù)使用遮婶、多...
    MinoyJet閱讀 3,922評(píng)論 1 15