Python 用函数重构经典策略模式
前言
合理使用作为一等对象的函数,可以使某种设计模式得以简化。
关于策略
定义一系列算法,把它们一一封装起来,并且使它们可以相互替换。
一个策略模式的示例
- 规则
1 有1000或以上积分的客户,每个订单享受5%的折扣。 2 同一个订单中,单个商品的数量达到20个或以上,享受10%折扣。 3 订单中的不同商品数达到10个或以上,享受7%折扣。
策略模式
- 上下文
把一些计算委托给实现不同算法的可互换组件,它提供服务。本例中,上下文是Order,它会根据不同的算法计算促销折扣
- 策略
实现不同算法的组件共同的接口。本例中Promotion这个抽象类扮演这个角色。
- 具体策略
策略的具体子类。本例中为 fidelityPromo, BulkPromo, LargeOrderPromo三个子类。
经典模式
#!/usr/bin/env python # -*- coding: utf-8 -*- # @File : ex1.py # @Time : 18/10/09 17:01 from abc import ABC, abstractmethod 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 = 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): fmt = '<Order total: {:.2f} due: {:.2f}>' return fmt.format(self.total(), self.due()) class Promotion(ABC): # 策略:抽象基类 @abstractmethod def discount(self, order): ''' 返回折扣金额(正值) :param order: :return: ''' class FidelityPromo(Promotion): # 第一个具体策略 ''' 为积分1000或以上的顾客提供5%折扣 ''' def discount(self, order): 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 # 两个顾客:joe的积分为0,ann的积分是1100 joe = Customer('John Doe', 0) ann = Customer('Ann Smith', 1100) # 有3个商品的购物车 cart = [LineItem('banana', 4, .5), LineItem('apple', 10, 1.5), LineItem('watermellon', 5, 5.0)] # joe未享受到折扣,ann享受到了5%折扣 ex1 = Order(joe, cart, FidelityPromo()) ex2 = Order(ann, cart, FidelityPromo()) # banana数量超过20个,joe享受到了10%的折扣 banana_cart = [LineItem('banana', 30, .5), LineItem('apple', 10, 1.5)] ex3 = Order(joe, banana_cart, BulkItemPromo()) # 商品数量超过了10个,为joe提供了7%的折扣 long_order = [LineItem(str(item_code), 1, 1.0) for item_code in range(10)] ex4 = Order(joe, long_order, LargeOrderPromo()) ex5 = Order(joe, cart, LargeOrderPromo()) print(ex1) print(ex2) print(ex3) print(ex4) print(ex5)
函数模式
#!/usr/bin/env python # -*- coding: utf-8 -*- # @File : ex2.py # @Time : 18/10/10 10:46 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 = 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 = '<Order total: {:.2f} due: {:.2f}>' return fmt.format(self.total(), self.due()) def fidelity_promo(order): return order.total() * .05 if order.customer.fidelity >= 1000 else 0 def bulk_item_promo(order): discount = 0 for item in order.cart: if item.quantity >= 20: discount += item.total() * .1 return discount def large_order_promo(order): distinct_items = {item.product for item in order.cart} if len(distinct_items) >= 10: return order.total() * .07 return 0 # 两个顾客:joe的积分为0,ann的积分是1100 joe = Customer('John Doe', 0) ann = Customer('Ann Smith', 1100) # 有3个商品的购物车 cart = [LineItem('banana', 4, .5), LineItem('apple', 10, 1.5), LineItem('watermellon', 5, 5.0)] eg1 = Order(joe, cart, fidelity_promo) eg2 = Order(ann, cart, fidelity_promo) banana_cart = [LineItem('banana', 30, .5), LineItem('apple', 10, 1.5)] eg3 = Order(joe, banana_cart, bulk_item_promo) long_order = [LineItem(str(item_code), 1, 1.0) for item_code in range(10)] eg4 = Order(joe, long_order, large_order_promo) print(eg1,'\n', eg2, '\n', eg3, '\n', eg4)
对比
经典模式中每个具体策略都是一个类,而且只定义了一个方法,即discount。此外,策略实例没有状态(实例属性)
使用函数代替抽象类,每个策略都是函数,不必实例化,拿来即用。新的Order类使用起来更简单,代码行数更少。
参考
<<流畅的Python>>
相关推荐
txlCandy 2020-04-20
tracy 2020-08-31
natloc 2020-07-18
Codeeror 2020-06-28
baike 2020-06-14
Ingram 2020-06-04
yishujixiaoxiao 2020-06-03
走在IT的路上 2020-05-01
清溪算法君老号 2020-04-14
wuxiaosi0 2020-02-22
shawsun 2020-02-14
spb 2020-02-14
xcguoyu 2020-02-14
wangxiaohua 2014-05-29
mbcsdn 2019-12-20
ustbfym 2019-12-01