<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
策略模式是一個經典的模式,簡化程式碼。
電商領域有個功能明細可以使用“策略”模式,就是根據客戶的屬性或訂單中的商品計算折扣。
比如一個網店,指定了以下的折扣規則, 並且一個訂單隻能享受一個折扣:
下面是UML類圖:
上下文:把一些計算委託給實現不同演演算法的可互換組建,他們提供服務。 在這個範例中,上下文就是Order,根據不同演演算法提供折扣。
策略:實現不同演演算法的元件共同介面。在這個範例中,Promotion的抽象類扮演這個角色。
具體策略:“策略”的子類,實現具體策略
範例,實現Order類,支援插入式折扣策略
import abc from collections import namedtuple from abc import abstractmethod Customer = namedtuple('Customer', 'name fidelity') # name:顧客名字 fidelity:忠誠度,這裡用積分體現 class LineItem: """單品資訊""" def __init__(self, product, quantity, price): self.product = product self.quantity = quantity self.price = price def total(self): return self.quantity * self.price 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.discount(self) return self.__total - discount def __repr__(self): return '<Order total:{:.2f} due:{:.2f}>'.format(self.total(), self.due()) # {:.2f}表示輸出小數點,保留2位小數 class Promotion(abc.ABC): """策略:抽象基礎類別""" @abstractmethod def discount(self, order): """返回折扣金額""" class FidelityPromo(Promotion): """具體策略:有1000積分以上的顧客,整個訂單可以享受5%的折扣""" def discount(self, order): return order.total() * 0.05 if order.customer.fidelity >= 1000 else 0 class BulkItemPromo(Promotion): """具體策略:同一個訂單中,單個商品的數量達到20個以上,單品享受10%折扣""" def discount(self, order): # return order.total() * 0.1 if any(item for item in order.cart if item.quantity >= 20) else 0 理解錯誤為整體折扣 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): return order.total() * 0.07 if len({item.product for item in order.cart}) >= 10 else 0
聊一下抽象基礎類別:
Python3.4中,宣告抽象基礎類別的最簡單的方式是繼承abc.ABC :class Promotion(abc.ABC):
Python3.0到Python3.3中,必須在class中使用metaclass=關鍵字 : class Promotion(metaclass=abc.ABCMeta):
Python2中,要在 類屬性中增加__metaclass__ = abc.ABCMeta
測試程式碼
# 兩個顧客,一個積分0,一個積分1100 joe = Customer('Joe', 0) ann = Customer('Ann', 1100) # 有三個商品的購物車 cart = [LineItem('banana', 4, .5), LineItem('apple', 10, 1.5), LineItem('watermelon', 5, 5.0)] # ann因為超過1000積分,獲得了5%折扣 order_joe = Order(joe, cart, FidelityPromo()) #這裡要FidelityPromo要加()來建立範例 print(order_joe) order_ann = Order(ann, cart, FidelityPromo()) print(order_ann) # 香蕉30個,蘋果10個。 banana_cart = [LineItem('banana', 30, .5), LineItem('apple', 10, 1.5)] # joe因為香蕉有30個,根據BulkItemPromo 香蕉優惠了1.5元 order_joe = Order(joe, banana_cart, BulkItemPromo()) print(order_joe)
列印
<Order total:42.00 due:42.00>
<Order total:42.00 due:39.90>
<Order total:30.00 due:28.50>
以上的模式中,每個具體策略都是一個類,而且只定義了一個方法:discount。此外他們的策略範例沒有範例屬性,看起來就像普通函數。
範例,使用函數實現折扣策略
Customer = namedtuple('Customer', 'name fidelity') # name:顧客名字 fidelity:忠誠度,這裡用積分體現 class LineItem: """單品資訊""" def __init__(self, product, quantity, price): self.product = product self.quantity = quantity self.price = price def total(self): return self.quantity * self.price 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): return '<Order total:{:.2f} due:{:.2f}>'.format(self.total(), self.due()) # {:.2f}表示輸出小數點,保留2位小數 def FidelityPromo(order): """具體策略:有1000積分以上的顧客,整個訂單可以享受5%的折扣""" return order.total() * 0.05 if order.customer.fidelity >= 1000 else 0 def BulkItemPromo(order): """具體策略:同一個訂單中,單個商品的數量達到20個以上,單品享受10%折扣""" discount = 0 for item in order.cart: if item.quantity >= 20: discount += item.total() * .1 return discount def LargeOrderPromo(order): """具體策略:訂單中不同商品的數量達到10個以上,整個訂單享受7%折扣""" return order.total() * 0.07 if len({item.product for item in order.cart}) >= 10 else 0 # 兩個顧客,一個積分0,一個積分1100 joe = Customer('Joe', 0) ann = Customer('Ann', 1100) # 有三個商品的購物車 cart = [LineItem('banana', 4, .5), LineItem('apple', 10, 1.5), LineItem('watermelon', 5, 5.0)] # ann因為超過1000積分,獲得了5%折扣 order_joe = Order(joe, cart, FidelityPromo) print(order_joe) order_ann = Order(ann, cart, FidelityPromo) print(order_ann) # 香蕉30個,蘋果10個。 banana_cart = [LineItem('banana', 30, .5), LineItem('apple', 10, 1.5)] # joe因為香蕉有30個,根據BulkItemPromo 香蕉優惠了1.5元 order_joe = Order(joe, banana_cart, BulkItemPromo) print(order_joe)
列印
<Order total:42.00 due:42.00>
<Order total:42.00 due:39.90>
<Order total:30.00 due:28.50>
以上可以看到,使用函數更加簡單,程式碼量減少。沒必要在新建訂單範例化新的促銷物件,函數拿來即用。
選擇最佳策略
要實現最佳折扣策略的自動選擇,只需要一個額外的函數即可。這樣就能自動找出最高的折扣。
def best_promotion(order): promotions = [FidelityPromo, BulkItemPromo, LargeOrderPromo] return max([func(order) for func in promotions])
自動找出模組中的全部策略
以上的promotions列表包含了三個策略,當再新增新的策略時,需要在這個列表中追加,使用以下寫法,可以自動識別策略函數:
範例,實現了把Promo結尾的函數參照,放進promotions列表中
def best_promotion(order): promotions = [globals()[key] for key in list(globals().keys()) if key.endswith('Promo')] return max([func(order) for func in promotions])
以上實現的原理就是利用globals()內建函數:
globals()返回一個字典,表示當前的全域性符號表。包含了當前所有定義的函數等。
自動找出模組中的全部策略-另一個種方案
把所有的策略函數,都放到一個模組(檔案)中,然後通過import匯入進來,再使用inspect模組提供的函數內省
範例,獲取一個模組中的所有函數
import promotions # 一個包含函數的模組 print(inspect.getmembers(promotions, inspect.isfunction)) # 獲取一個模組中的所有函數
列印
[('BulkItemPromo', <function BulkItemPromo at 0x0342F2B8>), ('FidelityPromo', <function FidelityPromo at 0x0342F228>), ('LargeOrderPromo', <function LargeOrderPromo at 0x0342F300>)]
範例,內省promotions模組,獲取所有策略函數
import promotions promotions = [func for func_name, func in inspect.getmembers(promotions, inspect.isfunction)] def best_promotion(order): return max([func(order) for func in promotions])
這樣的話,以後有新的策略,只需要在promotions模組中新增對應的策略函數,就可以自動載入了,妙!
命令模式的目的是解耦呼叫者(呼叫操作的物件)和接收者(提供實現的物件)。讓他們只實現一個方法execute介面,供呼叫者使用,這樣呼叫者無需瞭解接受者的介面,而且不同的接受者可以適應不同的Command子類。
範例,使用抽象類實現命令模式
import abc class Receiver: """命令的接收者,執行命令的地方""" def start(self): print('開始執行') def stop(self): print('停止執行') class Command(abc.ABC): """命令抽象類""" @abc.abstractmethod def execute(self): """命令物件對外只提供execute方法""" class StartCommand(Command): """開始執行的命令""" def __init__(self, receiver): self.receiver = receiver def execute(self): return self.receiver.start() class StopCommand(Command): """停止執行的命令""" def __init__(self, receiver): self.receiver = receiver def execute(self): return self.receiver.stop() class Client: """命令的呼叫者""" def __init__(self, command): self.command = command def __call__(self, *args, **kwargs): return self.command.execute() start = StartCommand(Receiver()) client = Client(start) client() stop = StopCommand(Receiver()) client = Client(stop) client()
列印
開始執行
停止執行
可以不使用抽象類,直接使用一個comman()函數來代理Command範例。使用一等函數對命令模式重新審視。
到此這篇關於Python設計模式中的策略模式詳解的文章就介紹到這了,更多相關Python策略模式內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45