python設計模式之工廠模式

sfencs發表於2018-11-28

一.理解工廠模式

在物件導向程式設計中,術語“工廠”表示一個負責建立替他型別物件的類。通常情況下,作為一個工廠的類有一個物件以及與它關聯的多個方法。客戶端使用某些引數呼叫此方法,之後,工廠會據此建立所需型別的物件,然後將它們返回給客戶端。

簡單理解:工廠模式依然是一種建立型設計模式,作為工廠,它所關心的是產品的產生,也就是物件的建立,我們利用工廠來建立物件,而不必我們親自建立物件,我們無需去理解如何建立物件,只需要向工廠提出要求,讓工廠去根據你的要求,給你生產你要的產品,給你相應的物件,這種模式便叫做工廠模式。

二.工廠模式的優點

  • 鬆耦合,物件的建立獨立於類的實現

  • 客戶端無需瞭解建立物件的類,只需知道需要傳遞的介面,方法和引數就能夠建立所需要的物件

  • 很容易擴充套件工廠新增其他型別物件的建立,而使用者只需要改變引數就可以了

三.python實現工廠模式

1.簡單工廠模式


from abc import ABCMeta,abstractmethod

class Coke(metaclass=ABCMeta):
    @abstractmethod
    def drink(self):
        pass

class Coca(Coke):
    def drink(self):
        print(`drink Coca-Cola`)

class Pepsi(Coke):
    def drink(self):
        print(`drink Pepsi-Cola`)

class Fast_food_restaurant():
    def make_coke(self ,name):
        return eval(name)()

KCD=Fast_food_restaurant()
coke=KCD.make_coke(`Coca`)
coke.drink()#drink Coca-Cola

ABCMeta是python的一個元類,用於在Python程式中建立抽象基類,抽象基類中宣告的抽象方法,使用abstractmethod裝飾器裝飾。

eval(類名)返回的是一個class型別的物件

我們建立一個可樂的抽象類,百事可樂和可口可樂繼承這個抽象類,我們又建立了快餐店類,也就是所說的工廠類,讓它生產可樂。當使用者需要可樂時,只需要告訴快餐店做一份什麼品牌的可樂,告訴快餐店可樂的名字,然後快餐店使用make_coke方法做可樂,返回了你所需要的物件——一杯可口可樂,然後就可以快樂的喝可樂了。。

2.工廠方法模式

簡單工廠模式已經幫我們做到我們需要某種物件時,可以不關心物件是怎麼建立的,只需要向工廠類要物件即可,但是如果我們又多了一種物件,例如又出現了一個可樂品牌,嗯,我們叫它sfencs可樂吧,那麼我們快餐店也得新新增這種可樂,也就是工廠類也得能夠建立sfencs可樂物件了,但是這樣就得在工廠類中加入新的邏輯判斷來根據使用者需求製造新新增的這個物件,顯然是不恰當的,因為這樣每當有一個新的型別的可樂增加的時候,我們都得修改工廠類的邏輯程式碼,使之能夠判斷出新的型別。這個問題使用工廠方法模式可以得到解決。

這裡有一個小問題,如上面的簡單工廠模式的程式碼,如果要新加sfencs可樂,似乎並不需要修改快餐店工廠類,只需要在sfencs可樂類定義之後,依然向make_coke函式傳遞類名即可,那這樣豈不是不需要工廠方法模式了嗎?

對於這個現象,其實是得益於eval()函式的功能,它能依據傳入的字串轉換成相應的類,也就是eval函式就是工廠類的邏輯判斷,如果不使用eval,那麼邏輯判斷可能就是多個if語句了,判斷條件就是輸入的字串引數是否等於這個,是否等於那個。。。等。但是eval使用也有很大的侷限性,它只能根據字串來判斷,我們不一定建立物件時都知道它的類名。除此之外,個人感覺eval這個函式有點厲害,越厲害就有可能造成不必要的麻煩,eval使用時還是得多注意。

接下來介紹工廠方法模式:


from abc import ABCMeta,abstractmethod

class Coke(metaclass=ABCMeta):
    @abstractmethod
    def drink(self):
        pass

class Coca(Coke):
    def drink(self):
        print(`drink Coca-Cola`)

class Pepsi(Coke):
    def drink(self):
        print(`drink Pepsi-Cola`)

class Sfencs(Coke):
    def drink(self):
        print(`drink Sfencs-Cola`)

class Fast_food_restaurant(metaclass=ABCMeta):
    @abstractmethod
    def make_coke(self):
        pass

class Coca_produce(Fast_food_restaurant):
    def make_coke(self):
        return Coca()

class Pepsi_produce(Fast_food_restaurant):
    def make_coke(self):
        return Pepsi()

class Sfencs_produce(Fast_food_restaurant):
    def make_coke(self):
        return Sfencs()

KCD=Sfencs_produce()
coke=KCD.make_coke()
coke.drink()#drink Sfencs-Cola

工廠方法模式將原來的工廠類變為了抽象類,不同型別的可樂通過不同的子類生產,也就是工廠方法模式定義了一個建立物件的介面,但具體建立哪個類的物件由子類來決定,這種方式的邏輯判斷相當於交給了客戶端,也就是KCD=Sfencs_produce()來選擇使用哪個子類,這樣如果有新可樂產品出現的話,只需要再寫一個子類繼承工廠抽象類。

這裡的類中,快餐店抽象類也叫做抽象工廠類,它的子類稱為具體工廠類。可樂也一樣,Coke為抽象產品類,它的子類為具體產品類。

3.抽象工廠模式

抽象工廠模式的主要目的是提供一個介面來建立一系列相關物件而無需指定具體的類。這個模式與與工廠方法模式的區別在於,它的一個方法子類,可以建立一系列的物件。

依然用可樂來舉例,只喝普通的可樂還不足以讓我們非常快樂,那麼如果有一杯冰可樂,想必就能滿足我們了。

於是我們的抽象產品類變為了兩個,一個是冰可樂,一個是普通可樂,具體產品類有百事冰可樂、可口可樂冰可樂,普通百事,普通可口可樂。抽象工廠類有生產冰可樂和生產普通可樂的抽象方法,具體工廠類有百事工廠,可口可樂工廠。


from abc import ABCMeta,abstractmethod

class Ice_coke(metaclass=ABCMeta):
    @abstractmethod
    def drink(self):
        pass

class Ordinary_coke(metaclass=ABCMeta):
    @abstractmethod
    def drink(self):
        pass

class Coca_ice(Ice_coke):
    def drink(self):
        print(`drink  Coca-ice-Cola`)

class Pepsi_ice(Ice_coke):
    def drink(self):
        print(`drink Pepsi-ice-Cola`)

class Coca_ordinary(Ordinary_coke):
    def drink(self):
        print(`drink Coca-ordinary-Cola`)

class Pepsi_ordinary(Ordinary_coke):
    def drink(self):
        print(`drink Pepsi-ordinary-Cola`)

class Fast_food_restaurant(metaclass=ABCMeta):
    @abstractmethod
    def make_ice_coke(self):
        pass

    @abstractmethod
    def make_ordinary_coke(self):
        pass

class Coca_produce(Fast_food_restaurant):
    def make_ice_coke(self):
        return Coca_ice()
    def make_ordinary_coke(self):
        return Coca_ordinary()

class Pepsi_produce(Fast_food_restaurant):
    def make_ice_coke(self):
        return Pepsi_ice()
    def make_ordinary_coke(self):
        return Pepsi_ordinary()

KCD=Coca_produce()
coke=KCD.make_ice_coke()
coke.drink()#drink  Coca-ice-Cola

這樣再有其他型別的可樂或者其他品牌的可樂,只需要新增方法或者類就可以了。


參考《Python設計模式(第2版)》

相關文章