問題:
在上一篇 python設計模式:工廠方法模式我們嘗試使用工廠方法建立了披薩店,現在為了保證披薩加盟店也能有良好的聲譽,我們需要統一原材料
,這個該如何做呢?
為了確保每家加盟店都是用高質量的原材料,我們打算建造一加原材料工廠,並將原材料運送到各個加盟店。每個加盟店會對原材料有不同的需求
,這裡我們就可以用上上一篇介紹的工廠方法模式了。
- 首先,建造原料工廠
- 然後建造區域的原料工廠(繼承自原料工廠)
- 在區域的原料工廠中實現原料的建立方法。
- 將原料工廠組合起來,加入到 PizzaStore(上一篇中由工廠方法實現)程式碼中。
按照這個思路,我們先建立原料工廠
建立原料工廠
建立原料工廠的實現程式碼如下:
# 原料
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()
。
實現 芝加哥芝士披薩
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 的原料也是從原料工廠直接獲取,現在我們控制了原料。
現在,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複製程式碼
通過上述程式碼可以看到我們做了以下工作:
- 引入了新型別的工廠(抽象工廠)來建立原料家族
- 通過抽象工廠提供的介面,我們建立了原料家族。
- 我們的原料程式碼從實際的 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) | 請我喝芬達 |
---|---|