完成一項任務每每有多種方式,咱們將其稱之爲策略。html
好比,超市作活動,若是你的購物積分滿1000,就能夠按兌換現金抵用券10元,若是購買同一商品滿10件,就能夠打9折,若是若是購買的金額超過500,就能夠享受滿減50元的優惠。這是三個不一樣的促銷策略。python
再好比,聯繫朋友、同窗,能夠打電話,也能夠發短信,能夠發微信,也能夠發郵件,這是四個不一樣的聯繫策略。設計模式
再好比,去外出旅遊,咱們能夠選擇火車,也能夠選擇公共汽車,能夠選擇飛機,也能夠選擇自駕遊。這又是四個不一樣的出行策略。微信
以上這些真實場景,都有策略選擇模型的影子,能夠考慮使用策略模式。app
經典的策略模式,是由三部分組成ide
以第一個超市作活動的場景來舉個例子。函數
首先是 Order 類:學習
class Item: def __init__(self, issue, price, quantity): self.issue = issue self.price = price self.quantity = quantity def total(self): return self.price * self.quantity class Order: def __init__(self, customer, promotion=None): self.cart = [] self.customer = customer self.promotion = promotion def add_to_cart(self, *items): for item in items: self.cart.append(item) def total(self): total = 0 for item in self.cart: total += item.total() return total def due(self): if not self.promotion: discount = 0 else: discount = self.promotion.discount(self) return (self.total() - discount)
而後是積分兌換現金券的策略,爲了保證咱們的代碼具備良好的可擴展性及維護性,我會先寫一個策略類,它是一個抽象基類,它的子類都是一個具體的策略,都必須實現 discount
方法,就好比我們的積分兌換現金策略。優化
from abc import ABC, abstractmethod class Promotion(ABC): @abstractmethod def discount(self, order): pass class FidelityPromo(Promotion): ''' 若是積分滿1000分,就能夠兌換10元現金券 ''' def discount(self, order): return 10 if order.customer.fidelity >1000 else 0
假設如今小明去商場買了一件衣服(600塊),兩雙鞋子(200*2),他的購物積分有1500點。編碼
在平時,商場通常都沒有活動,可是終年都有積分換現金抵用券的活動。
>>> from collections import namedtuple # 定義兩個字段:名字,購物積分 >>> Customer = namedtuple('Customer', 'name fidelity') >>> xm = Customer('小明', 1500) >>> item1 = Item('鞋子', 200, 3) >>> item2 = Item('衣服', 600, 1) >>> order = Order(xm, FidelityPromo()) >>> order.add_to_cart(item1, item2) # 原價 1200,用上積分後,只要1190 >>> order <Order Total:1200 due:1190>
眼看着,五一節也快了,商場準備大搞促銷
有了此前咱們使用 策略模式
打下的基礎,咱們並非使用硬編碼的方式來配置策略,因此不須要改動太多的源碼,只要直接定義五一節的兩個促銷策略類便可(一樣繼承自 Promotion 抽象基類),就像插件同樣,即插即用。
class BulkItemPromo(Promotion): ''' 若是單項商品購買10件,便可9折。 ''' def discount(self, order): discount = 0 for item in order.cart: if item.quantity >= 10: discount += item.total() * 0.1 return discount class LargeOrderPromo(Promotion): ''' 若是訂單總金額大於等於500,就能夠立減50 ''' def discount(self, order): discount = 0 if order.total() >= 500: discount = 50 return discount
看到商場活動如此給力,小明的錢包也鼓了起來,開始屯起了生活用品。
若是使用了第一個策略,原價600,只須要花 580
>>> from collections import namedtuple >>> Customer = namedtuple('Customer', 'name fidelity') >>> xm = Customer('小明', 300) >>> item1 = Item('紙巾', 20, 10) >>> item2 = Item('食用油', 50, 4) >>> item3 = Item('牛奶', 50, 4) >>> order = Order(xm, BulkItemPromo()) >>> order.add_to_cart(item1, item2, item3) >>> order <Order Total:600 due:580.0>
若是使用了第二個策略,原價600,只須要花550
>>> from collections import namedtuple >>> Customer = namedtuple('Customer', 'name fidelity') >>> xm = Customer('小明', 300) >>> item1 = Item('紙巾', 20, 10) >>> item2 = Item('食用油', 50, 4) >>> item3 = Item('牛奶', 50, 4) >>> order = Order(xm, LargeOrderPromo()) >>> order.add_to_cart(item1, item2, item3) >>> order <Order Total:600 due:550>
兩個策略即插即用,只須要在前臺下訂單時,選擇對應的策略便可,原業務邏輯無需改動。
>>> order = Order(xm, BulkItemPromo()) >>> order = Order(xm, LargeOrderPromo())
可是問題很快又來了,商場搞活動,卻讓顧客手動選擇使用哪一個優惠策略,做爲一個良心的商家,應該要能自動對比全部策略得出最優惠的價格來給到顧客。這就要求後臺代碼要可以找出當前可用的所有策略,並一一比對摺扣。
# 找出全部的促銷策略 all_promotion = [globals()[name] for name in globals() if name.endswith('Promo') and name != 'BestPromo'] # 實現一個最優策略類 class BestPromo(Promotion): def discount(self, order): # 找出當前文件中全部的策略 all_promotion = [globals()[name] for name in globals() if name.endswith('Promo') and name != 'BestPromo'] # 計算最大折扣 return max([promo().discount(order) for promo in all_promotion])
在前臺下訂單的時候,就會自動計算全部的優惠策略,直接告訴顧客最便宜的價格。
# 直接選擇這個最優策略 >>> order = Order(xm, BestPromo()) >>> order.add_to_cart(item1, item2, item3) >>> order <Order Total:600 due:550>
經過以上例子,能夠總結出使用策略模式
的好處
但同時,策略模式 也會帶來一些弊端。
對於以上的例子,仔細一想,其實還有很多能夠優化的地方。
好比,爲了實現經典的模式,咱們先要定義一個抽象基類,再實現具體的策略類。對於上面這樣一個簡單的計算折扣價格邏輯來講,其實能夠用函數來實現,而後在實例化 Order 類時指定這個策略函數便可,大可沒必要將類給搬出來。這樣就能夠避免在下訂單時,不斷的建立策略對象,減小多餘的運行時消耗。這裏就不具體寫出代碼了。
因此學習設計模式,不只要知道如何利用這樣的模式組織代碼,更要領會其思想,活學活用,靈活變通。
以上,就是今天關於 策略模式
的一些我的分享,若有講得不到位的,還請後臺留言指正!
原文出處:https://www.cnblogs.com/wongbingming/p/10753072.html