摘要:在復(fù)雜的情況下,需要具體策略維護(hù)內(nèi)部狀態(tài)時,可能需要把策略和享元模式結(jié)合起來。函數(shù)比用戶定義的類的實(shí)例輕量,而且無需使用享元模式,因?yàn)楦鱾€策略函數(shù)在編譯模塊時只會創(chuàng)建一次。
一等函數(shù)實(shí)現(xiàn)設(shè)計(jì)模式 經(jīng)典的“策略”模式定義
定義一系列算法,把它們一一封裝起來,并且使它們可以相互替
換。本模式使得算法可以獨(dú)立于使用它的客戶而變化。
假如一個網(wǎng)店制定了下述折扣規(guī)則。
有 1000 或以上積分的顧客,每個訂單享 5% 折扣。
同一訂單中,單個商品的數(shù)量達(dá)到 20 個或以上,享 10% 折扣。
訂單中的不同商品達(dá)到 10 個或以上,享 7% 折扣。
簡單起見,我們假定一個訂單一次只能享用一個折扣。
具體策略由上下文類的客戶選擇。
實(shí)例化訂單之前,系統(tǒng)會以某種方式選擇一種促銷折扣策略,
然后把它傳給 Order 構(gòu)造方法。
具體怎么選擇策略,不在這個模式的職責(zé)范圍內(nèi)。
from abc import ABC, abstractclassmethod from collections import namedtuple Customer = namedtuple("Customer", "name fidelity") # 類似于購物車 class LineItem(object): def __init__(self, product, quantity, price): self.product = product self.quantity = quantity self.price = price def total(self): return self.price * self.quantity # 上下文是 Order class Order(object): def __init__(self, customer, cart, promotion=None): self.customer = customer self.cart = list(cart) self.promotion = promotion def total(self): ###4 if not hasattr(self, "__total"): self.__total = sum(item.total() for item in self.cart) return self.__total def due(self): ###5 if self.promotion is None: discount = 0 else: discount = self.promotion.discount(self) ###6 return self.total() - discount def __repr__(self): ###2 fmt = "使用函數(shù)實(shí)現(xiàn)“策略”模式" return fmt.format(self.total(), self.due()) ###3 # 策略:抽象基類 class Promotion(ABC): @abstractclassmethod def discount(self, order): """返回折扣金額(正值)""" class FidelityPromo(Promotion): # 第一個具體策略 """為積分為1000或以上的顧客提供5%折扣""" def discount(self, order): ###7 return order.total() * .05 if order.customer.fidelity >= 1000 else 0 class BulkItemPromo(Promotion): # 第二個具體策略 """單個商品為20個或以上時提供10%折扣""" def discount(self, order): discount = 0 for item in order.cart: if item.quantity >= 20: discount += item.total() * .1 return discount class LargeOrderPromo(Promotion): # 第三個具體策略 """訂單中的不同商品達(dá)到10個或以上時提供7%折扣""" def discount(self, order): distinct_items = {item.product for item in order.cart} if len(distinct_items) >= 10: return order.total() * .07 return 0 if __name__ == "__main__": # 兩位顧客 一個積分是0 一個是1100 longe = Customer("longe", 0) liang = Customer("Ann Smith", 1100) # 購物的商品 cart = [LineItem("banana", 4, .5), LineItem("apple", 10, 1.5), LineItem("watermellon", 5, 5.0)] # 這樣去調(diào)用 print(Order(longe, cart, FidelityPromo())) ###111 print(Order(liang, cart, FidelityPromo())) banana_cart = [LineItem("banana", 30, .5), LineItem("apple", 10, 1.5)] print(Order(longe, banana_cart, BulkItemPromo())) long_order = [LineItem(str(item_code), 1, 1.0) for item_code in range(10)] print(Order(longe, long_order, LargeOrderPromo()))
from collections import namedtuple Customer = namedtuple("Customer", "name fidelity") class LineItem: def __init__(self, product, quantity, price): self.product = product self.quantity = quantity self.price = price def total(self): return self.price * self.quantity class Order: # 上下文 def __init__(self, customer, cart, promotion=None): self.customer = customer self.cart = list(cart) self.promotion = promotion def total(self): if not hasattr(self, "__total"): self.__total = sum(item.total() for item in self.cart) return self.__total def due(self): if self.promotion is None: discount = 0 else: discount = self.promotion(self) return self.total() - discount def __repr__(self): fmt = "策略模式 思想" return fmt.format(self.total(), self.due()) def fidelity_promo(order): """為積分為1000或以上的顧客提供5%折扣""" return order.total() * .05 if order.customer.fidelity >= 1000 else 0 def bulk_item_promo(order): """單個商品為20個或以上時提供10%折扣""" discount = 0 for item in order.cart: if item.quantity >= 20: discount += item.total() * .1 return discount def large_order_promo(order): """訂單中的不同商品達(dá)到10個或以上時提供7%折扣""" distinct_items = {item.product for item in order.cart} if len(distinct_items) >= 10: return order.total() * .07 return 0 joe = Customer("John Doe", 0) ann = Customer("Ann Smith", 1100) cart = [LineItem("banana", 4, .5),LineItem("apple", 10, 1.5),LineItem("watermellon", 5, 5.0)] print(Order(joe, cart, fidelity_promo)) print(Order(ann, cart, fidelity_promo)) banana_cart = [LineItem("banana", 30, .5),LineItem("apple", 10, 1.5)] print(Order(joe, banana_cart, bulk_item_promo)) long_order = [LineItem(str(item_code), 1, 1.0) for item_code in range(10)] print(Order(joe, long_order, large_order_promo)) print(Order(joe, cart, large_order_promo))
策略對象通常是很好的享元(flyweight)
享元是可共享的對象,可以同時在多個上下文中使用。
共享是推薦的做法,這樣不必在每個新的上下文(這里是Order 實(shí)例)中使用相同的策略時不斷新建具體策略對象,從而減少消耗。
需要具體策略維護(hù)內(nèi)部狀態(tài)時,可能需要把“策略”和“享元”模式結(jié)合起來。
但是,具體策略一般沒有內(nèi)部狀態(tài),只是處理上下文中的數(shù)據(jù)。此時,一定要使用普通的函數(shù),別去編寫只有一個方法的類,再去實(shí)現(xiàn)另一個類聲明的單函數(shù)接口。
函數(shù)比用戶定義的類的實(shí)例輕量,而且無需使用“享元”模式,因?yàn)楦鱾€策略函數(shù)在 Python編譯模塊時只會創(chuàng)建一次。
普通的函數(shù)也是“可共享的對象,可以同時在多個上下文中使用”。
選擇最佳策略:簡單的方式 選擇折扣最大的策略 (新增策略時會改代碼)from collections import namedtuple Customer = namedtuple("Customer", "name fidelity") class LineItem: def __init__(self, product, quantity, price): self.product = product self.quantity = quantity self.price = price def total(self): return self.price * self.quantity class Order: # 上下文 def __init__(self, customer, cart, promotion=None): self.customer = customer self.cart = list(cart) self.promotion = promotion def total(self): if not hasattr(self, "__total"): self.__total = sum(item.total() for item in self.cart) return self.__total def due(self): if self.promotion is None: discount = 0 else: discount = self.promotion(self) return self.total() - discount def __repr__(self): fmt = "不想改代碼 找出模塊中的全部策略" return fmt.format(self.total(), self.due()) def fidelity_promo(order): """為積分為1000或以上的顧客提供5%折扣""" return order.total() * .05 if order.customer.fidelity >= 1000 else 0 def bulk_item_promo(order): """單個商品為20個或以上時提供10%折扣""" discount = 0 for item in order.cart: if item.quantity >= 20: discount += item.total() * .1 return discount def large_order_promo(order): """訂單中的不同商品達(dá)到10個或以上時提供7%折扣""" distinct_items = {item.product for item in order.cart} if len(distinct_items) >= 10: return order.total() * .07 return 0 joe = Customer("John Doe", 0) ann = Customer("Ann Smith", 1100) cart = [LineItem("banana", 4, .5), LineItem("apple", 10, 1.5), LineItem("watermellon", 5, 5.0)] banana_cart = [LineItem("banana", 30, .5), LineItem("apple", 10, 1.5)] long_order = [LineItem(str(item_code), 1, 1.0) for item_code in range(10)] ## 找出最大的折扣 promos = [fidelity_promo, bulk_item_promo, large_order_promo] def best_promo(order): """選擇可用的最佳折扣""" # print([promo(order) for promo in promos]) return max(promo(order) for promo in promos) print(Order(joe, long_order, best_promo)) print(Order(joe, banana_cart, best_promo)) print(Order(ann, cart, best_promo) )
新策略名字結(jié)尾必須是_promo
使用 globals 函數(shù)幫助 best_promo 自動找到其他可用的*_promo 函數(shù),過程有點(diǎn)曲折
內(nèi)省模塊的全局命名空間,構(gòu)建 promos 列表
過濾掉 best_promo 自身,防止無限遞歸。
promos = [globals()[name] for name in globals() if name.endswith("_promo") and name != "best_promo"] print(promos)另一種方法
另一個可行的方法是將所有的策略函數(shù)都存放在一個多帶帶的模塊中
除了 best_promo,這里我們將 3 個策略函數(shù)存放在了 promotions.py 中
下面的代碼中,最大的變化時內(nèi)省名為 promotions 的獨(dú)立模塊,構(gòu)建策略函數(shù)列表。
注意,下面要導(dǎo)入 promotions 模塊,以及高階內(nèi)省函數(shù)的 inspect 模塊
import inspect import promotions promos = [func for name, func in inspect.getmembers(promotions, inspect.isfunction)] def best_promo(order): """選擇可用的最佳折扣 """ return max(promo(order) for promo in promos) print(Order(joe, long_order, best_promo)) print(Order(joe, banana_cart, best_promo)) print(Order(ann, cart, best_promo) )命令模式
命令模式的目的是解耦調(diào)用操作的對象(調(diào)用者)和提供實(shí)現(xiàn)的對象(接收者)
在《設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》所舉的示例中,調(diào)用者是圖形應(yīng)用程序中的菜單項(xiàng),而接收者是被編輯的文檔或應(yīng)用程序自身。
模式的做法這個模式的做法是,在二者之間放一個 Command 對象,讓它實(shí)現(xiàn)只有一個方法(execute)的接口,調(diào)用接收者中的方法執(zhí)行所需的操作。
這樣,調(diào)用者無需了解接收者的接口,而且不同的接收者可以適應(yīng)不同的 Command 子類。
調(diào)用者有一個具體的命令,通過調(diào)用 execute 方法執(zhí)行。
MacroCommand 的各個實(shí)例都在內(nèi)部存儲著命令列表class MacroCommand: """一個執(zhí)行一組命令的命令""" def __init__(self, commands): self.commands = list(commands) def __call__(self): for command in self.commands: command()小總結(jié)
1.python 對某些設(shè)計(jì)默認(rèn) 可以用純函數(shù)來實(shí)現(xiàn), 不用可以去寫類
2.設(shè)計(jì)模式得看看了
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/41656.html
摘要:第六章抽象本章會介紹如何將語句組織成函數(shù)。關(guān)鍵字參數(shù)和默認(rèn)值目前為止,我們使用的參數(shù)都是位置參數(shù),因?yàn)樗鼈兊奈恢煤苤匾聦?shí)上比它們的名字更重要。參數(shù)前的星號將所有值放置在同一個元祖中。函數(shù)內(nèi)的變量被稱為局部變量。 第六章:抽象 本章會介紹如何將語句組織成函數(shù)。還會詳細(xì)介紹參數(shù)(parameter)和作用域(scope)的概念,以及遞歸的概念及其在程序中的用途。 懶惰即美德 斐波那契數(shù)...
摘要:可以通過定位參數(shù)和關(guān)鍵字參數(shù)傳入的形參多數(shù)函數(shù)的參數(shù)屬于此類。就像數(shù)據(jù)格式化一樣數(shù)據(jù)帶上標(biāo)簽自行創(chuàng)建函數(shù)它會自行創(chuàng)建函數(shù)。創(chuàng)建的函數(shù)會在對象上調(diào)用參數(shù)指定的方法自己創(chuàng)建函數(shù)凍結(jié)參數(shù)這個高階函數(shù)用于部分應(yīng)用一個函數(shù)。 高階函數(shù) 接受函數(shù)為參數(shù),或者把函數(shù)作為結(jié)果返回的函數(shù)是高階函數(shù) def reverse(word): return word[::-1] ...
摘要:創(chuàng)建一個新對象將構(gòu)造函數(shù)的作用域賦給新對象因此就指向了這個新對象執(zhí)行構(gòu)造函數(shù)中的代碼為這個新對象添加屬性返回新對象。 本章內(nèi)容 理解對象屬性 理解并創(chuàng)建對象 理解繼承 ECMA-262把對象定義為:無序?qū)傩缘募?,其屬性可以包含基本值、對象或者函?shù) 理解對象 創(chuàng)建對象 創(chuàng)建自定義對象的最簡單方式就是創(chuàng)建一個Object的實(shí)例,再為它添加屬性和方法。 var person = new...
摘要:繼承的是超類型中構(gòu)造函數(shù)中的屬性,如上繼承了屬性,但沒有繼承原型中的方法。上述造成的結(jié)果是子類型實(shí)例中有兩組超類型的構(gòu)造函數(shù)中定義的屬性,一組在子類型的實(shí)例中,一組在子類型實(shí)例的原型中。 ECMAScript只支持實(shí)現(xiàn)繼承,主要依靠原型鏈來實(shí)現(xiàn)。與實(shí)現(xiàn)繼承對應(yīng)的是接口繼承,由于script中函數(shù)沒有簽名,所以無法實(shí)現(xiàn)接口繼承。 一、原型鏈 基本思想:利用原型讓一個引用類型繼承另一個引用...
摘要:當(dāng)前狀態(tài)可以使用函數(shù)確定,該函數(shù)會返回下述字符串中的一個。解釋器正在執(zhí)行。打印消息,然后協(xié)程終止,導(dǎo)致生成器對象拋出異常。實(shí)例運(yùn)行完畢后,返回的值綁定到上。 協(xié)程 協(xié)程可以身處四個狀態(tài)中的一個。 當(dāng)前狀態(tài)可以使用inspect.getgeneratorstate(...) 函數(shù)確定,該函數(shù)會返回下述字符串中的一個。 GEN_CREATED 等待開始執(zhí)行。 GEN_RUNNING 解...
閱讀 857·2021-11-18 10:07
閱讀 2367·2021-10-14 09:42
閱讀 5376·2021-09-22 15:45
閱讀 602·2021-09-03 10:29
閱讀 3483·2021-08-31 14:28
閱讀 1887·2019-08-30 15:56
閱讀 3050·2019-08-30 15:54
閱讀 1005·2019-08-29 11:32