返回介绍

7.3 使用装饰器改进“策略”模式

发布于 2024-02-05 21:59:47 字数 1536 浏览 0 评论 0 收藏 0

使用注册装饰器可以改进 6.1 节中的电商促销折扣示例。

回顾一下,示例 6-6 的主要问题是,定义体中有函数的名称,但是 best_promo 用来判断哪个折扣幅度最大的 promos 列表中也有函数名称。这种重复是个问题,因为新增策略函数后可能会忘记把它添加到 promos 列表中,导致 best_promo 忽略新策略,而且不报错,为系统引入了不易察觉的缺陷。示例 7-3 使用注册装饰器解决了这个问题。

示例 7-3 promos 列表中的值使用 promotion 装饰器填充

promos = []  ➊

def promotion(promo_func):  ➋
  promos.append(promo_func)
  return promo_func

@promotion  ➌
def fidelity(order):
  """为积分为1000或以上的顾客提供5%折扣"""
  return order.total() * .05 if order.customer.fidelity >= 1000 else 0

@promotion
def bulk_item(order):
  """单个商品为20个或以上时提供10%折扣"""
  discount = 0
  for item in order.cart:
    if item.quantity >= 20:
      discount += item.total() * .1
  return discount

@promotion
def large_order(order):
  """订单中的不同商品达到10个或以上时提供7%折扣"""
  distinct_items = {item.product for item in order.cart}
  if len(distinct_items) >= 10:
    return order.total() * .07
  return 0

def best_promo(order):  ➍
  """选择可用的最佳折扣
  """
  return max(promo(order) for promo in promos)

❶ promos 列表起初是空的。

❷ promotion 把 promo_func 添加到 promos 列表中,然后原封不动地将其返回。

❸ 被 @promotion 装饰的函数都会添加到 promos 列表中。

❹ best_promos 无需修改,因为它依赖 promos 列表。

与 6.1 节给出的方案相比,这个方案有几个优点。

促销策略函数无需使用特殊的名称(即不用以 _promo 结尾)。

@promotion 装饰器突出了被装饰的函数的作用,还便于临时禁用某个促销策略:只需把装饰器注释掉。

促销折扣策略可以在其他模块中定义,在系统中的任何地方都行,只要使用 @promotion 装饰即可。

不过,多数装饰器会修改被装饰的函数。通常,它们会定义一个内部函数,然后将其返回,替换被装饰的函数。使用内部函数的代码几乎都要靠闭包才能正确运作。为了理解闭包,我们要退后一步,先了解 Python 中的变量作用域。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文