完成一項任務每每有多種方式,咱們將其稱之爲策略。python
好比,超市作活動,若是你的購物積分滿1000,就能夠按兌換現金抵用券10元,若是購買同一商品滿10件,就能夠打9折,若是若是購買的金額超過500,就能夠享受滿減50元的優惠。這是三個不一樣的促銷策略。設計模式
再好比,聯繫朋友、同窗,能夠打電話,也能夠發短信,能夠發微信,也能夠發郵件,這是四個不一樣的聯繫策略。微信
再好比,去外出旅遊,咱們能夠選擇火車,也能夠選擇公共汽車,能夠選擇飛機,也能夠選擇自駕遊。這又是四個不一樣的出行策略。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點。spa
在平時,商場通常都沒有活動,可是終年都有積分換現金抵用券的活動。
>>> 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 類時指定這個策略函數便可,大可沒必要將類給搬出來。這樣就能夠避免在下訂單時,不斷的建立策略對象,減小多餘的運行時消耗。這裏就不具體寫出代碼了。
因此學習設計模式,不只要知道如何利用這樣的模式組織代碼,更要領會其思想,活學活用,靈活變通。
以上,就是今天關於 策略模式
的一些我的分享,若有講得不到位的,還請後臺留言指正!