python設計模式-抽象工廠模式

問題:在上一篇 python設計模式:工廠方法模式咱們嘗試使用工廠方法建立了披薩店,如今爲了保證披薩加盟店也能有良好的聲譽,咱們須要統一原材料,這個該如何作呢?python

爲了確保每家加盟店都是用高質量的原材料,咱們打算建造一加原材料工廠,並將原材料運送到各個加盟店。每一個加盟店會對原材料有不一樣的需求,這裏咱們就能夠用上上一篇介紹的工廠方法模式了。git

  1. 首先,建造原料工廠
  2. 而後建造區域的原料工廠(繼承自原料工廠)
  3. 在區域的原料工廠中實現原料的建立方法。
  4. 將原料工廠組合起來,加入到 PizzaStore(上一篇中由工廠方法實現)代碼中。

按照這個思路,咱們先建立原料工廠github

建立原料工廠

建立原料工廠的實現代碼以下:django

# 原料
class FreshClams:

    def __str__(self):
        return 'Fresh Clams'

class MarinaraSauce:

    def __str__(self):
        return "Marinara Sauce"

class ThickCrustDough:

    def __str__(self):
        return "Thick Crust Dough"

class ReggianoCheese:

    def __str__(self):
        return "Reggiano Cheese"

class SlicedPepperoni:

    def __str__(self):
        return "Sliced Pepperoni"

class Garlic:

    def __str__(self):
        return "Garlic"

class Onion:

    def __str__(self):
        return "Onion"

class RedPepper:

    def __str__(self):
        return "Red Pepper"

# 披薩店原料工廠
class PizzaIngredientFactory:

    ''' 定義原料工廠 '''

    def create_dough(self):
        raise NotImplementedError()

    def create_sauce(self):
        raise NotImplementedError()

    def create_cheese(self):
        raise NotImplementedError()

    def create_pepperoni(self):
        raise NotImplementedError()

    def create_clam(self):
        raise NotImplementedError()

    def create_veggies(self):
        raise NotImplementedError()複製代碼

在這個工廠中,每一個原料都是一個方法,原料的實現須要在具體的原料工廠中實現。
這裏每一個原料方法沒有作任何工做,只是拋出了NotImplementedError 這樣作是爲了強制子類從新實現相應的方法,若是不從新實現用到時就會拋出 NotImplementedError。設計模式

固然也能夠把 PizzaIngredientFactory 的 metaclass 設置成 abc.ABCMeta 這樣的話,這個類就是真正的抽象基類。性能

建立紐約原料工廠

class NYPizzaIngredientFactory(PizzaIngredientFactory):
    def create_dough(self):
        print("Tossing %s" % ThickCrustDough())
        return ThickCrustDough()

    def create_sauce(self):
        print("Adding %s..." % MarinaraSauce())
        return MarinaraSauce()

    def create_cheese(self):
        print("Adding %s..." % ReggianoCheese())
        return ReggianoCheese()

    def create_pepperoni(self):
        print("Adding %s..." % SlicedPepperoni())
        return SlicedPepperoni()

    def create_clam(self):
        print("Adding %s..." % FreshClams())
        return FreshClams()

    def create_veggies(self):
        # 蔬菜可能有多種,這裏使用列表
        veggies = [Garlic(), Onion(), RedPepper()]
        for veggie in veggies:
            print(" %s" % veggie)
        return veggies複製代碼

對於原料家族的每一種原料,咱們都提供了原料的紐約版本。優化

重作 Pizza 類

class Pizza:

    name = None
    dough = None
    sauce = None
    cheese = None
    veggies = []
    pepperoni = None
    clam = None

    def prepare(self):
        raise NotImplementedError()

    def bake(self):
        print("Bake for 25 minutes at 350")

    def cut(self):
        print("Cutting the pizza into diagonal slices")

    def box(self):
        print("Place pizza in official PizzaStore box")

    def __str__(self):
        return self.name複製代碼

上述代碼和工廠方法的代碼相比,只是把 prepare() 方法抽象出來,須要相應的 具體的 pizza 類來實現 prepare()ui

實現 芝加哥芝士披薩

class NYStyleCheesePizza(Pizza):

    def prepare(self):
        dough = self.ingredient_factory.create_dough()
        sauce = self.ingredient_factory.create_sauce()
        cheese = self.ingredient_factory.create_cheese()
        clam = self.ingredient_factory.create_clam()
        veggies = self.ingredient_factory.create_veggies()複製代碼

從上述代碼能夠發現,Pizza 的原料也是從原料工廠直接獲取,如今咱們控制了原料。spa

如今,Pizza 類不須要關心原料,只須要負責製做 pizza 就好。Pizza 和原料被解耦。設計

從新實現 PizzaStore

class PizzaStore:

    # 須要聲明原料工廠
    ingredient_factory = None

    def create_pizza(self, pizza_type):
        # 每一個須要子類實現的方法都會拋出NotImplementedError
        # 咱們也能夠把 PizzaStore 的 metaclass 設置成 abc.ABCMeta
        # 這樣的話,這個類就是真正的抽象基類
        raise NotImplementedError()

    def order_pizza(self, pizza_type):  # 如今把 pizza 的類型傳入 order_pizza()

        pizza = self.create_pizza(pizza_type)

        # 一旦咱們有了一個 pizza,須要作一些準備(擀麪皮、加佐料),而後烘烤、切片、裝盒
        pizza.prepare()
        pizza.bake()
        pizza.cut()
        pizza.box()
        return pizza

class NYStylePizzStore(PizzaStore):

    # 將須要用到的原料工廠賦值給變量 ingredient_factory
    ingredient_factory = NYPizzaIngredientFactory()

    def create_pizza(self, pizza_type):
        # 根據 pizza 類型,咱們實例化正確的具體類,而後將其賦值給 pizza 實例變量
        if pizza_type == 'cheese':
            pizza = NYStyleCheesePizza('NY Style Sauce and Cheese Pizza',
                                       self.ingredient_factory)
        elif pizza_type == 'clam':
            pizza = NYStyleClamPizza('NY Style Clam Pizza',
                                     self.ingredient_factory)
        return pizza複製代碼

經過上述代碼能夠看到咱們作了如下工做:

  1. 引入了新類型的工廠(抽象工廠)來建立原料家族
  2. 經過抽象工廠提供的接口,咱們建立了原料家族。
  3. 咱們的原料代碼從實際的 Pizza 工廠中成功解耦,能夠應用到不一樣地方,響應的,咱們能夠方便的替換原料工廠來生產不一樣的 pizza。

來看下下單的代碼

def main():
    nystore = NYStylePizzStore()
    pizza = nystore.order_pizza('cheese')
    print('*' * 10)
    print("goodspeed ordered a %s" % pizza)
    print('*' * 10)複製代碼

和工廠方法的代碼相比,沒有任何改變。

[源碼參考python-design-patter-abstract-factory.py](https://gist.github.com/gusibi/5e0797f5458678322486f999ca87a180)

抽象工廠模式

抽象工廠模式提供一個接口,用於建立相關或依賴對象的家族,而不須要指定具體類。

也就是說,抽象工廠容許客戶使用抽象的接口來建立一組相關的產品,而不須要知道實際產出的具體產品是什麼,這樣依賴,客戶就從具體產品中被解耦。

歸納來講就是,抽象工廠是邏輯上的一組工廠方法,每一個工廠方法各司其職,負責生產不一樣種類的對象。

咱們來看下 抽象工廠模式 的類圖:

抽象工廠模式類圖
抽象工廠模式類圖

抽象工廠在 django_factory 中應用比較多,有興趣的能夠看下源碼。

抽象工廠模式 和 工廠方法模式 的比較

抽象工廠模式 和 工廠方法模式 都是負責建立對象,但

  • 工廠方法模式使用的是繼承
  • 抽象工廠模式使用的是對象的組合

這也就意味着利用工廠方法建立對象須要擴展一個類,並覆蓋它的工廠方法(負責將客戶從具體類中解耦)。
抽象工廠提供一個用來建立產品家族的抽象類型,這個類型的子類定義了產品被產生的方法。要想使用這個工廠(NYPizzaIngredientFactory),必須先實例化它(ingredient_factory = NYPizzaIngredientFactory()),而後將它傳入一些針對抽象類型所寫的代碼中(也作到了將客戶從具體產品中解耦),同時還把一羣相關的產品集合起來。

工廠方法模式和抽象工廠模式如何選擇

開始的時候,能夠選擇工廠方法模式,由於他很簡單(只須要繼承,並實現工廠方法便可)。若是後來發現應用須要用到多個工廠方法,那麼是時候使用抽象工廠模式了,它能夠把相關的工廠方法組合起來。

抽象工廠模式優勢和缺點

優勢

  • 能夠將客戶從具體產品中解耦
  • 抽象工廠可讓對象建立更容易被追蹤
  • 同時將對象建立與使用解耦
  • 也能夠優化內存佔用提高應用性能

缺點

由於抽象工廠是將一組相關的產品集合起來,若是須要擴展這組產品,就須要改變接口,而改變接口則意味着須要改變每一個子類的接口

參考連接


最後,感謝女友支持。

歡迎關注(April_Louisa) 請我喝芬達
歡迎關注
歡迎關注
請我喝芬達
請我喝芬達
相關文章
相關標籤/搜索