Python项目代码优化,优化你的 if-else!

浏览: 3518

在上篇文章《项目代码太多if-else?这样优化才优雅!》中,我们已经介绍了优化if-else条件逻辑的7个实用技巧。本文接着上篇文章的主题,以某电商平台618活动订单优惠计算逻辑优化为例,和大家分享使用策略模式优化if-else代码块的方法。

这个需求很简单

一大早我的咖啡还没喝完,就被产品拉去开会提需求了,产品给出了平台今年618大促的折扣规则:

•88Vip 95折•每满300减40•买3件免单1件

她给我提了一个需求,让我把这些优惠活动的逻辑加到订单结算系统中,如果顾客参与了某个活动,在支付的时候,就要减去这些折扣。

这个实现好简单

这个简单啊!不就是加几个if-else,根据不用的优惠方式计算折扣么?我一顿操作5分钟写好了代码!

我先定义了一个顾客和产品类:

    from collections import namedtuple

    Customer = namedtuple('Customer', 'name is_88vip')
    Product = namedtuple('Product', 'name price')

    因为顾客和产品是纯数据类,只有属性没有任何方法,所以我使用了collections模块下的namedtuple来实现。

    接下来是重点内容了,我设计了一个订单类,订单信息中包含顾客名,购物清单,以及该订单享受的优惠活动。

      class Order:
      def __init__(self, customer, cart, promotion=None):
      """订单类

      Args:
      customer (Customer): 该订单的顾客
      cart (list): 购物车列表
      promotion (str, optional): 该订单享受的优惠,默认为 None
      """
      self.customer = customer
      self.cart = list(cart)
      self.promotion = promotion

      def total(self):
      """订单优惠前总额"""
      if not hasattr(self, '__total'):
      self.__total = sum(item.price for item in self.cart)
      return self.__total

      def due(self):
      """订单优惠后总额"""
      if self.promotion is None:
      discount = 0
      # 88vip打95折
      elif self.promotion=='vip_discount':
      discount = self.total() * .05 if self.customer.is_88vip else 0
      # 每满300减40
      elif self.promotion=='full_credit_discount':
      total = 0
      for item in self.cart:
      total += item.price
      discount = (total//300) * 40
      # 满3减免单一件
      elif self.promotion=='free_one_discount':
      if len(self.cart) >= 3:
      discount = min([item.price for item in self.cart])
      else:
      discount = 0
      return self.total() - discount

      def __repr__(self):
      fmt = '<订单 总价: {:.2f} 实付: {:.2f}>'
      return fmt.format(self.total(), self.due())

      那么怎么计算优惠呢?

      我在Order类中加了一个due()方法,该方法中有一大段的if-else语句,根据不同的优惠策略,计算订单优惠后的总额。比如如果是88vip, 就在原来总额的基础上打95折。

      很快代码就写好了,我就交付上线了。

      客户端调用:

        joe = Customer('John Doe', True)
        cart_A = [Product('banana', 4),
        Product('apple', 10),
        Product('watermellon', 5)]

        print('策略一:为88vip顾客提供5%折扣')
        print(Order(joe, cart_A, "vip_discount"))

        cart_B = [Product('banana', 630),
        Product('apple', 410)]

        print('策略二:每满300减40活动')
        print(Order(joe, cart_B, "full_credit_discount"))

        cart_C = [Product('banana', 630),
        Product('apple', 410),
        Product('watermellon', 210)]

        print('策略三:满3件免单最便宜的一件')
        print(Order(joe, cart_C, "free_one_discount"))

        运行结果:

          (base) root\Python> main.py
          策略一:为88vip顾客提供5%折扣
          <订单 总价: 19.00 实付: 18.05>

          策略二:每满300减40活动
          <订单 总价: 1040.00 实付: 920.00>

          策略三:满3件免单最便宜的一件
          <订单 总价: 1250.00 实付: 1040.00>

          改需求了!

          过了2天,产品又过来找我了,说市场部又策划了30个优惠活动,你再把优惠计算逻辑这些加上去吧!(发个图大家感受下!)

          image.png

          我打开代码,准备无脑加几个if-else的时候,突然意识到一件很严重的问题:

          我这次改好if-else, 如果哪天又上了新的优惠策略,岂不是我又要加一个if-else?鬼知道她们还会不会天天改需求,现在这样子做根本没有扩展性。这不符合设计模式中的对扩展开放,对修改关闭的原则。

          我隔壁的同事小王说,加一个if-else也没有什么大不了的事情呀?不用那复杂吧?

          但是,目前我们只有3个优惠策略,一个方法的代码已经20行了,如果我们有30个策略,这个方法可能就要300行了!从另外一个角度讲,这不符合单一职责原则。

          所以,考虑到系统后期的扩展,和代码的测试和维护,我决定重新设计一下我的代码了。

          使用策略模式拯救if-else

          我发现Order类的due()这个方法的if-else逻辑实在是太多了,每个if-else都是一种具体的折扣逻辑,他们完成的是同样的一件事情——计算折扣,只是具体行为有些差异而已,所以这是典型的策略模式的应用场景啊!

          因此第一步我需要将这些具体的优惠策略提炼为独立的函数。

            def vip_discount(order):
            """淘宝88vip客户95折"""
            return order.total() * .05 if order.customer.is_88vip else 0


            def full_credit_discount(order):
            """每满300减40"""
            total = 0
            for item in order.cart:
            total += item.price
            discount = (total//300) * 40
            return discount


            def free_one_discount(order):
            """满3件商品免单最便宜的一件"""
            if len(order.cart) >= 3:
            return min([item.price for item in order.cart])
            return 0

            这些方法都接受一个Order对象作为参数,并且使用Order对象提供的信息,决定如何计算订单的优惠。我们把这些参数提炼到独立的函数以后,下面就可以着手去修改Order类的due()方法了。

              class Order:
              def __init__(self, customer, cart, promotion=None):
              """订单类

              Args:
              customer (Customer): 该订单的顾客
              cart (list): 购物车列表
              promotion (function, optional): 该订单折扣的计算方法
              """
              self.customer = customer
              self.cart = list(cart)
              self.promotion = promotion

              def total(self):
              """订单优惠前总额"""
              if not hasattr(self, '__total'):
              self.__total = sum(item.price 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 = '<订单 总价: {:.2f} 实付: {:.2f}>'
              return fmt.format(self.total(), self.due())

              Order类的改动也不复杂,因为在Python中,函数可以作为参数对象进行传递,所以现在的Order类的promotion参数不再接受一个字符串对象了,改为接收一个订单优惠计算策略的函数对象。然后,我在due()方法中,可以直接引用通过promotion传入的函数对象,来实现订单优惠的计算了。

              下面是客户端的调用代码:

                joe = Customer('John Doe', True)

                cart_A = [Product('banana', 4),
                Product('apple', 10),
                Product('watermellon', 5)]

                print('策略一:为88vip顾客提供5%折扣')
                print(Order(joe, cart_A, vip_discount))

                cart_B = [Product('banana', 630),
                Product('apple', 410)]

                print('策略二:每满300减40活动')
                print(Order(joe, cart_B, full_credit_discount))

                cart_C = [Product('banana', 630),
                Product('apple', 410),
                Product('watermellon', 210)]

                print('策略三:满3件免单最便宜的一件')
                print(Order(joe, cart_C, free_one_discount))

                运行结果:

                  (base) root\Python> main.py
                  策略一:为88vip顾客提供5%折扣
                  <订单 总价: 19.00 实付: 18.05>

                  策略二:每满300减40活动
                  <订单 总价: 1040.00 实付: 920.00>

                  策略三:满3件免单最便宜的一件
                  <订单 总价: 1250.00 实付: 1040.00>

                  总结

                  在本篇文章中,我们主要的目的是讨论使用策略模式优化if-else逻辑的方法,当然,使用策略模式的理由远不仅仅是 为了优化if-else。在所有设计模式中,策略模式是最简单也是应用最广的一种设计模式,在实际应用中,策略模式还适用以下场景:

                  •如果有多种方式完成同一件事情,那就是策略模式大展拳脚的时候了。比如我们的示例中有多种优惠计算方法。•一个类或者方法中有太多的if-else代码逻辑。这通常暗示着这个类或者方法承担了太多的职责,违反了软件设计中的单一职责原则,使用策略模式,将可以帮助我们将这些职责分离开来。•最后,使用策略模式还可以帮助我们解耦行为和使用这些行为的类,便于扩展和维护。

                  推荐 0
                  本文由 ID王大伟 创作,采用 知识共享署名-相同方式共享 3.0 中国大陆许可协议 进行许可。
                  转载、引用前需联系作者,并署名作者且注明文章出处。
                  本站文章版权归原作者及原出处所有 。内容为作者个人观点, 并不代表本站赞同其观点和对其真实性负责。本站是一个个人学习交流的平台,并不用于任何商业目的,如果有任何问题,请及时联系我们,我们将根据著作权人的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。

                  0 个评论

                  要回复文章请先登录注册