摘要:在復雜的情況下,需要具體策略維護內部狀態時,可能需要把策略和享元模式結合起來。函數比用戶定義的類的實例輕量,而且無需使用享元模式,因為各個策略函數在編譯模塊時只會創建一次。
一等函數實現設計模式 經典的“策略”模式定義
定義一系列算法,把它們一一封裝起來,并且使它們可以相互替
換。本模式使得算法可以獨立于使用它的客戶而變化。
假如一個網店制定了下述折扣規則。
有 1000 或以上積分的顧客,每個訂單享 5% 折扣。
同一訂單中,單個商品的數量達到 20 個或以上,享 10% 折扣。
訂單中的不同商品達到 10 個或以上,享 7% 折扣。
簡單起見,我們假定一個訂單一次只能享用一個折扣。
具體策略由上下文類的客戶選擇。
實例化訂單之前,系統會以某種方式選擇一種促銷折扣策略,
然后把它傳給 Order 構造方法。
具體怎么選擇策略,不在這個模式的職責范圍內。
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 = "使用函數實現“策略”模式" 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): # 第三個具體策略 """訂單中的不同商品達到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)] # 這樣去調用 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): """訂單中的不同商品達到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 實例)中使用相同的策略時不斷新建具體策略對象,從而減少消耗。
需要具體策略維護內部狀態時,可能需要把“策略”和“享元”模式結合起來。
但是,具體策略一般沒有內部狀態,只是處理上下文中的數據。此時,一定要使用普通的函數,別去編寫只有一個方法的類,再去實現另一個類聲明的單函數接口。
函數比用戶定義的類的實例輕量,而且無需使用“享元”模式,因為各個策略函數在 Python編譯模塊時只會創建一次。
普通的函數也是“可共享的對象,可以同時在多個上下文中使用”。
選擇最佳策略:簡單的方式 選擇折扣最大的策略 (新增策略時會改代碼)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): """訂單中的不同商品達到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) )
新策略名字結尾必須是_promo
使用 globals 函數幫助 best_promo 自動找到其他可用的*_promo 函數,過程有點曲折
內省模塊的全局命名空間,構建 promos 列表
過濾掉 best_promo 自身,防止無限遞歸。
promos = [globals()[name] for name in globals() if name.endswith("_promo") and name != "best_promo"] print(promos)另一種方法
另一個可行的方法是將所有的策略函數都存放在一個多帶帶的模塊中
除了 best_promo,這里我們將 3 個策略函數存放在了 promotions.py 中
下面的代碼中,最大的變化時內省名為 promotions 的獨立模塊,構建策略函數列表。
注意,下面要導入 promotions 模塊,以及高階內省函數的 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) )命令模式
命令模式的目的是解耦調用操作的對象(調用者)和提供實現的對象(接收者)
在《設計模式:可復用面向對象軟件的基礎》所舉的示例中,調用者是圖形應用程序中的菜單項,而接收者是被編輯的文檔或應用程序自身。
模式的做法這個模式的做法是,在二者之間放一個 Command 對象,讓它實現只有一個方法(execute)的接口,調用接收者中的方法執行所需的操作。
這樣,調用者無需了解接收者的接口,而且不同的接收者可以適應不同的 Command 子類。
調用者有一個具體的命令,通過調用 execute 方法執行。
MacroCommand 的各個實例都在內部存儲著命令列表class MacroCommand: """一個執行一組命令的命令""" def __init__(self, commands): self.commands = list(commands) def __call__(self): for command in self.commands: command()小總結
1.python 對某些設計默認 可以用純函數來實現, 不用可以去寫類
2.設計模式得看看了
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/41656.html
摘要:第六章抽象本章會介紹如何將語句組織成函數。關鍵字參數和默認值目前為止,我們使用的參數都是位置參數,因為它們的位置很重要,事實上比它們的名字更重要。參數前的星號將所有值放置在同一個元祖中。函數內的變量被稱為局部變量。 第六章:抽象 本章會介紹如何將語句組織成函數。還會詳細介紹參數(parameter)和作用域(scope)的概念,以及遞歸的概念及其在程序中的用途。 懶惰即美德 斐波那契數...
摘要:可以通過定位參數和關鍵字參數傳入的形參多數函數的參數屬于此類。就像數據格式化一樣數據帶上標簽自行創建函數它會自行創建函數。創建的函數會在對象上調用參數指定的方法自己創建函數凍結參數這個高階函數用于部分應用一個函數。 高階函數 接受函數為參數,或者把函數作為結果返回的函數是高階函數 def reverse(word): return word[::-1] ...
摘要:創建一個新對象將構造函數的作用域賦給新對象因此就指向了這個新對象執行構造函數中的代碼為這個新對象添加屬性返回新對象。 本章內容 理解對象屬性 理解并創建對象 理解繼承 ECMA-262把對象定義為:無序屬性的集合,其屬性可以包含基本值、對象或者函數 理解對象 創建對象 創建自定義對象的最簡單方式就是創建一個Object的實例,再為它添加屬性和方法。 var person = new...
摘要:繼承的是超類型中構造函數中的屬性,如上繼承了屬性,但沒有繼承原型中的方法。上述造成的結果是子類型實例中有兩組超類型的構造函數中定義的屬性,一組在子類型的實例中,一組在子類型實例的原型中。 ECMAScript只支持實現繼承,主要依靠原型鏈來實現。與實現繼承對應的是接口繼承,由于script中函數沒有簽名,所以無法實現接口繼承。 一、原型鏈 基本思想:利用原型讓一個引用類型繼承另一個引用...
摘要:當前狀態可以使用函數確定,該函數會返回下述字符串中的一個。解釋器正在執行。打印消息,然后協程終止,導致生成器對象拋出異常。實例運行完畢后,返回的值綁定到上。 協程 協程可以身處四個狀態中的一個。 當前狀態可以使用inspect.getgeneratorstate(...) 函數確定,該函數會返回下述字符串中的一個。 GEN_CREATED 等待開始執行。 GEN_RUNNING 解...
閱讀 837·2021-11-18 10:07
閱讀 2355·2021-10-14 09:42
閱讀 5315·2021-09-22 15:45
閱讀 585·2021-09-03 10:29
閱讀 3462·2021-08-31 14:28
閱讀 1873·2019-08-30 15:56
閱讀 3038·2019-08-30 15:54
閱讀 994·2019-08-29 11:32