摘要:源碼參考抽象工廠模式抽象工廠模式提供一個(gè)接口,用于創(chuàng)建相關(guān)或依賴(lài)對(duì)象的家族,而不需要指定具體類(lèi)。工廠方法模式和抽象工廠模式如何選擇開(kāi)始的時(shí)候,可以選擇工廠方法模式,因?yàn)樗芎?jiǎn)單只需要繼承,并實(shí)現(xiàn)工廠方法即可。
問(wèn)題:在上一篇 python設(shè)計(jì)模式:工廠方法模式我們嘗試使用工廠方法創(chuàng)建了披薩店,現(xiàn)在為了保證披薩加盟店也能有良好的聲譽(yù),我們需要統(tǒng)一原材料,這個(gè)該如何做呢?
為了確保每家加盟店都是用高質(zhì)量的原材料,我們打算建造一加原材料工廠,并將原材料運(yùn)送到各個(gè)加盟店。每個(gè)加盟店會(huì)對(duì)原材料有不同的需求,這里我們就可以用上上一篇介紹的工廠方法模式了。
首先,建造原料工廠
然后建造區(qū)域的原料工廠(繼承自原料工廠)
在區(qū)域的原料工廠中實(shí)現(xiàn)原料的創(chuàng)建方法。
將原料工廠組合起來(lái),加入到 PizzaStore(上一篇中由工廠方法實(shí)現(xiàn))代碼中。
按照這個(gè)思路,我們先創(chuàng)建原料工廠
創(chuàng)建原料工廠創(chuàng)建原料工廠的實(shí)現(xiàn)代碼如下:
# 原料 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()
在這個(gè)工廠中,每個(gè)原料都是一個(gè)方法,原料的實(shí)現(xiàn)需要在具體的原料工廠中實(shí)現(xiàn)。
這里每個(gè)原料方法沒(méi)有做任何工作,只是拋出了NotImplementedError 這樣做是為了強(qiáng)制子類(lèi)重新實(shí)現(xiàn)相應(yīng)的方法,如果不重新實(shí)現(xiàn)用到時(shí)就會(huì)拋出 NotImplementedError。
創(chuàng)建紐約原料工廠當(dāng)然也可以把 PizzaIngredientFactory 的 metaclass 設(shè)置成 abc.ABCMeta 這樣的話,這個(gè)類(lèi)就是真正的抽象基類(lèi)。
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
對(duì)于原料家族的每一種原料,我們都提供了原料的紐約版本。
重做 Pizza 類(lèi)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() 方法抽象出來(lái),需要相應(yīng)的 具體的 pizza 類(lèi)來(lái)實(shí)現(xiàn) prepare()。
實(shí)現(xiàn) 芝加哥芝士披薩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()
從上述代碼可以發(fā)現(xiàn),Pizza 的原料也是從原料工廠直接獲取,現(xiàn)在我們控制了原料。
重新實(shí)現(xiàn) PizzaStore現(xiàn)在,Pizza 類(lèi)不需要關(guān)心原料,只需要負(fù)責(zé)制作 pizza 就好。Pizza 和原料被解耦。
class PizzaStore: # 需要聲明原料工廠 ingredient_factory = None def create_pizza(self, pizza_type): # 每個(gè)需要子類(lèi)實(shí)現(xiàn)的方法都會(huì)拋出NotImplementedError # 我們也可以把 PizzaStore 的 metaclass 設(shè)置成 abc.ABCMeta # 這樣的話,這個(gè)類(lèi)就是真正的抽象基類(lèi) raise NotImplementedError() def order_pizza(self, pizza_type): # 現(xiàn)在把 pizza 的類(lèi)型傳入 order_pizza() pizza = self.create_pizza(pizza_type) # 一旦我們有了一個(gè) pizza,需要做一些準(zhǔn)備(搟面皮、加佐料),然后烘烤、切片、裝盒 pizza.prepare() pizza.bake() pizza.cut() pizza.box() return pizza class NYStylePizzStore(PizzaStore): # 將需要用到的原料工廠賦值給變量 ingredient_factory ingredient_factory = NYPizzaIngredientFactory() def create_pizza(self, pizza_type): # 根據(jù) pizza 類(lèi)型,我們實(shí)例化正確的具體類(lèi),然后將其賦值給 pizza 實(shí)例變量 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
通過(guò)上述代碼可以看到我們做了以下工作:
引入了新類(lèi)型的工廠(抽象工廠)來(lái)創(chuàng)建原料家族
通過(guò)抽象工廠提供的接口,我們創(chuàng)建了原料家族。
我們的原料代碼從實(shí)際的 Pizza 工廠中成功解耦,可以應(yīng)用到不同地方,響應(yīng)的,我們可以方便的替換原料工廠來(lái)生產(chǎn)不同的 pizza。
來(lái)看下下單的代碼def main(): nystore = NYStylePizzStore() pizza = nystore.order_pizza("cheese") print("*" * 10) print("goodspeed ordered a %s" % pizza) print("*" * 10)
和工廠方法的代碼相比,沒(méi)有任何改變。
[源碼參考python-design-patter-abstract-factory.py](https://gist.github.com/gusibi/5e0797f5458678322486f999ca87a180)
抽象工廠模式抽象工廠模式提供一個(gè)接口,用于創(chuàng)建相關(guān)或依賴(lài)對(duì)象的家族,而不需要指定具體類(lèi)。
也就是說(shuō),抽象工廠允許客戶(hù)使用抽象的接口來(lái)創(chuàng)建一組相關(guān)的產(chǎn)品,而不需要知道實(shí)際產(chǎn)出的具體產(chǎn)品是什么,這樣依賴(lài),客戶(hù)就從具體產(chǎn)品中被解耦。
概括來(lái)說(shuō)就是,抽象工廠是邏輯上的一組工廠方法,每個(gè)工廠方法各司其職,負(fù)責(zé)生產(chǎn)不同種類(lèi)的對(duì)象。
我們來(lái)看下 抽象工廠模式 的類(lèi)圖:
抽象工廠在 django_factory 中應(yīng)用比較多,有興趣的可以看下源碼。
抽象工廠模式 和 工廠方法模式 的比較抽象工廠模式 和 工廠方法模式 都是負(fù)責(zé)創(chuàng)建對(duì)象,但
工廠方法模式使用的是繼承
抽象工廠模式使用的是對(duì)象的組合
這也就意味著利用工廠方法創(chuàng)建對(duì)象需要擴(kuò)展一個(gè)類(lèi),并覆蓋它的工廠方法(負(fù)責(zé)將客戶(hù)從具體類(lèi)中解耦)。
抽象工廠提供一個(gè)用來(lái)創(chuàng)建產(chǎn)品家族的抽象類(lèi)型,這個(gè)類(lèi)型的子類(lèi)定義了產(chǎn)品被產(chǎn)生的方法。要想使用這個(gè)工廠(NYPizzaIngredientFactory),必須先實(shí)例化它(ingredient_factory = NYPizzaIngredientFactory()),然后將它傳入一些針對(duì)抽象類(lèi)型所寫(xiě)的代碼中(也做到了將客戶(hù)從具體產(chǎn)品中解耦),同時(shí)還把一群相關(guān)的產(chǎn)品集合起來(lái)。
開(kāi)始的時(shí)候,可以選擇工廠方法模式,因?yàn)樗芎?jiǎn)單(只需要繼承,并實(shí)現(xiàn)工廠方法即可)。如果后來(lái)發(fā)現(xiàn)應(yīng)用需要用到多個(gè)工廠方法,那么是時(shí)候使用抽象工廠模式了,它可以把相關(guān)的工廠方法組合起來(lái)。
抽象工廠模式優(yōu)點(diǎn)和缺點(diǎn) 優(yōu)點(diǎn)可以將客戶(hù)從具體產(chǎn)品中解耦
抽象工廠可以讓對(duì)象創(chuàng)建更容易被追蹤
同時(shí)將對(duì)象創(chuàng)建與使用解耦
也可以?xún)?yōu)化內(nèi)存占用提升應(yīng)用性能
缺點(diǎn)因?yàn)槌橄蠊S是將一組相關(guān)的產(chǎn)品集合起來(lái),如果需要擴(kuò)展這組產(chǎn)品,就需要改變接口,而改變接口則意味著需要改變每個(gè)子類(lèi)的接口
參考鏈接python設(shè)計(jì)模式:工廠方法模式
python-design-patter-abstract-factory.py https://gist.github.com/gusibi/5e0797f5458678322486f999ca87a180
最后,感謝女朋友支持。
歡迎關(guān)注(April_Louisa) | 請(qǐng)我喝芬達(dá) |
---|---|
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/40974.html
摘要:所謂的產(chǎn)品族,一般或多或少的都存在一定的關(guān)聯(lián),抽象工廠模式就可以在類(lèi)內(nèi)部對(duì)產(chǎn)品族的關(guān)聯(lián)關(guān)系進(jìn)行定義和描述,而不必專(zhuān)門(mén)引入一個(gè)新的類(lèi)來(lái)進(jìn)行管理。 0x01.定義與類(lèi)型 定義:抽象工廠模式提供一個(gè)創(chuàng)建一系列相關(guān)或相互依賴(lài)對(duì)象的接口 無(wú)需指定它們具體的類(lèi) 類(lèi)型:創(chuàng)建型 UML showImg(https://segmentfault.com/img/bVbtBp1?w=800&h=862...
摘要:本文會(huì)用實(shí)現(xiàn)三種工廠模式的簡(jiǎn)單例子,所有代碼都托管在上。工廠方法模式繼承了簡(jiǎn)單工廠模式的優(yōu)點(diǎn)又有所改進(jìn),其不再通過(guò)一個(gè)工廠類(lèi)來(lái)負(fù)責(zé)所有產(chǎn)品的創(chuàng)建,而是將具體創(chuàng)建工作交給相應(yīng)的子類(lèi)去做,這使得工廠方法模式可以允許系統(tǒng)能夠更高效的擴(kuò)展。 前言 工廠模式,顧名思義就是我們可以通過(guò)一個(gè)指定的工廠獲得需要的產(chǎn)品,在設(shè)計(jì)模式中主要用于抽象對(duì)象的創(chuàng)建過(guò)程,讓用戶(hù)可以指定自己想要的對(duì)象而不必關(guān)心對(duì)象的...
摘要:工廠方法模式可以幫助我們將產(chǎn)品的實(shí)現(xiàn)從使用中解耦。應(yīng)用中使用工廠模式的例子的模塊使用工廠方法模式來(lái)創(chuàng)建表單字段。也使用到了工廠方法模式。中不同數(shù)據(jù)庫(kù)連接部分也用到了工廠方法模式。 題目:假設(shè)你有一個(gè) pizza 店,功能包括下訂單、做 pizza,你的代碼會(huì)如何寫(xiě)呢? def order_pizza(): pizza = Pizza() pizza.prepare() ...
摘要:通過(guò)工廠方法模式的類(lèi)圖可以看到,工廠方法模式有四個(gè)要素工廠接口工廠接口是工廠方法模式的核心,與調(diào)用者直接交互用來(lái)提供產(chǎn)品。使用場(chǎng)景創(chuàng)建類(lèi)模式,在任何需要生成復(fù)雜對(duì)象的地方,都可以使用工廠方法模式。 0x01.定義與類(lèi)型 定義:定義一個(gè)創(chuàng)建對(duì)象的接口,但讓實(shí)現(xiàn)這個(gè)接口的類(lèi)來(lái)決定實(shí)例化那個(gè)類(lèi),工廠方法讓類(lèi)的實(shí)例化推遲到子類(lèi)中進(jìn)行 類(lèi)型:創(chuàng)建型 uml類(lèi)圖 showImg(https:/...
摘要:在工廠方法模式中,我們會(huì)遇到一個(gè)問(wèn)題,當(dāng)產(chǎn)品非常多時(shí),繼續(xù)使用工廠方法模式會(huì)產(chǎn)生非常多的工廠類(lèi)。從簡(jiǎn)單工廠模式到抽象工廠模式,我們都是在用后一種模式解決前一種模式的缺陷,都是在最大程度降低代碼的耦合性。 單例模式 所謂單例模式,也就是說(shuō)不管什么時(shí)候我們要確保只有一個(gè)對(duì)象實(shí)例存在。很多情況下,整個(gè)系統(tǒng)中只需要存在一個(gè)對(duì)象,所有的信息都從這個(gè)對(duì)象獲取,比如系統(tǒng)的配置對(duì)象,或者是線程池。這些...
閱讀 3054·2021-11-23 09:51
閱讀 3834·2021-11-22 15:29
閱讀 3294·2021-10-08 10:05
閱讀 1620·2021-09-22 15:20
閱讀 1042·2019-08-30 15:56
閱讀 1137·2019-08-30 15:54
閱讀 788·2019-08-26 11:54
閱讀 2690·2019-08-26 11:32