完成一项任务每每有多种方式,咱们将其称之为策略。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点。插件
在平时,商场通常都没有活动,可是终年都有积分换现金抵用券的活动。
>>> 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 类时指定这个策略函数便可,大可没必要将类给搬出来。这样就能够避免在下订单时,不断的建立策略对象,减小多余的运行时消耗。这里就不具体写出代码了。
因此学习设计模式,不只要知道如何利用这样的模式组织代码,更要领会其思想,活学活用,灵活变通。
以上,就是今天关于 策略模式
的一些我的分享,若有讲得不到位的,还请后台留言指正!