python实现策略模式

系统 1699 0

python实现策略模式

 

原文地址

1、策略模式概述

策略模式 :定义一系列算法,把它们一一封装起来,并且使它们之间可以相互替换。此模式让算法的变化不会影响到使用算法的客户。

电商领域有个使用“策略”模式的经典案例,即根据客户的属性或订单中的商品计算折扣。

假如一个网店制定了下述折扣规则。

  • 有 1000 或以上积分的顾客,每个订单享 5% 折扣。

  • 同一订单中,单个商品的数量达到 20 个或以上,享 10% 折扣。

  • 订单中的不同商品达到 10 个或以上,享 7% 折扣。

简单起见,我们假定一个订单一次只能享用一个折扣。

UML类图如下:

python实现策略模式_第1张图片

 

Promotion 抽象类提供了不同算法的公共接口,fidelityPromo、BulkPromo 和 LargeOrderPromo 三个子类实现具体的“策略”,具体策略由上下文类的客户选择。

在这个示例中,实例化订单(Order 类)之前,系统会以某种方式选择一种促销折扣策略,然后把它传给 Order 构造方法。具体怎么选择策略,不在这个模式的职责范围内。(选择策略可以使用工厂模式。)

 

 2、传统方法实现策略模式:

            
              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 
            
            =
            
               list(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 
            
            = 
            
              '
            
            
              <订单 总价: {:.2f} 实付: {:.2f}>
            
            
              '
            
            
              return
            
            
               fmt.format(self.total(), self.due())



            
            
              class
            
             Promotion(ABC):  
            
              #
            
            
               策略:抽象基类
            
            
                  @abstractmethod
    
            
            
              def
            
            
               discount(self, order):
        
            
            
              """
            
            
              返回折扣金额(正值)
            
            
              """
            
            
              class
            
             FidelityPromo(Promotion):  
            
              #
            
            
               第一个具体策略
            
            
              """
            
            
              为积分为1000或以上的顾客提供5%折扣
            
            
              """
            
            
              def
            
            
               discount(self, order):
        
            
            
              return
            
             order.total() * 0.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() * 0.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() * 0.07
        
            
              return
            
            
               0


joe 
            
            = Customer(
            
              '
            
            
              John Doe
            
            
              '
            
            
              , 0)
ann 
            
            = Customer(
            
              '
            
            
              Ann Smith
            
            
              '
            
            , 1100
            
              )

cart 
            
            = [LineItem(
            
              '
            
            
              banana
            
            
              '
            
            , 4, 0.5
            
              ),
        LineItem(
            
            
              '
            
            
              apple
            
            
              '
            
            , 10, 1.5
            
              ),
        LineItem(
            
            
              '
            
            
              watermellon
            
            
              '
            
            , 5, 5.0
            
              )]


            
            
              print
            
            (
            
              '
            
            
              策略一:为积分为1000或以上的顾客提供5%折扣
            
            
              '
            
            
              )

            
            
              print
            
            
              (Order(joe, cart, FidelityPromo()))

            
            
              print
            
            
              (Order(ann, cart, FidelityPromo()))

banana_cart 
            
            = [LineItem(
            
              '
            
            
              banana
            
            
              '
            
            , 30, 0.5
            
              ),
               LineItem(
            
            
              '
            
            
              apple
            
            
              '
            
            , 10, 1.5
            
              )]


            
            
              print
            
            (
            
              '
            
            
              策略二:单个商品为20个或以上时提供10%折扣
            
            
              '
            
            
              )

            
            
              print
            
            
              (Order(joe, banana_cart, BulkItemPromo()))

long_order 
            
            = [LineItem(str(item_code), 1, 1.0) 
            
              for
            
             item_code 
            
              in
            
             range(10
            
              )]


            
            
              print
            
            (
            
              '
            
            
              策略三:订单中的不同商品达到10个或以上时提供7%折扣
            
            
              '
            
            
              )

            
            
              print
            
            
              (Order(joe, long_order, LargeOrderPromo()))

            
            
              print
            
            (Order(joe, cart, LargeOrderPromo()))
          

输出:

            策略一:为积分为1000或以上的顾客提供5%
            
              折扣

            
            <订单 总价: 42.00 实付: 42.00>
<订单 总价: 42.00 实付: 39.90>
            
              
策略二:单个商品为20个或以上时提供10
            
            %
            
              折扣

            
            <订单 总价: 30.00 实付: 28.50>
            
              
策略三:订单中的不同商品达到10个或以上时提供7
            
            %
            
              折扣

            
            <订单 总价: 10.00 实付: 9.30>
<订单 总价: 42.00 实付: 42.00>
          

 

 

3、使用函数实现策略模式

在传统策略模式中,每个具体策略都是一个类,而且都只定义了一个方法,除此之外没有其他任何实例属性。它们看起来像是普通的函数一样。的确如此,在 Python 中,我们可以把具体策略换成了简单的函数,并且去掉策略的抽象类。

            
              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 
            
            =
            
               list(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 
            
            = 
            
              '
            
            
              <订单 总价: {:.2f} 实付: {:.2f}>
            
            
              '
            
            
              return
            
            
               fmt.format(self.total(), self.due())



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



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



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


joe 
            
            = Customer(
            
              '
            
            
              John Doe
            
            
              '
            
            
              , 0)
ann 
            
            = Customer(
            
              '
            
            
              Ann Smith
            
            
              '
            
            , 1100
            
              )

cart 
            
            = [LineItem(
            
              '
            
            
              banana
            
            
              '
            
            , 4, 0.5
            
              ),
        LineItem(
            
            
              '
            
            
              apple
            
            
              '
            
            , 10, 1.5
            
              ),
        LineItem(
            
            
              '
            
            
              watermellon
            
            
              '
            
            , 5, 5.0
            
              )]


            
            
              print
            
            (
            
              '
            
            
              策略一:为积分为1000或以上的顾客提供5%折扣
            
            
              '
            
            
              )

            
            
              print
            
            
              (Order(joe, cart, fidelity_promo))

            
            
              print
            
            
              (Order(ann, cart, fidelity_promo))

banana_cart 
            
            = [LineItem(
            
              '
            
            
              banana
            
            
              '
            
            , 30, 0.5
            
              ),
               LineItem(
            
            
              '
            
            
              apple
            
            
              '
            
            , 10, 1.5
            
              )]


            
            
              print
            
            (
            
              '
            
            
              策略二:单个商品为20个或以上时提供10%折扣
            
            
              '
            
            
              )

            
            
              print
            
            
              (Order(joe, banana_cart, bulk_item_promo))

long_order 
            
            = [LineItem(str(item_code), 1, 1.0) 
            
              for
            
             item_code 
            
              in
            
             range(10
            
              )]


            
            
              print
            
            (
            
              '
            
            
              策略三:订单中的不同商品达到10个或以上时提供7%折扣
            
            
              '
            
            
              )

            
            
              print
            
            
              (Order(joe, long_order, large_order_promo))

            
            
              print
            
            (Order(joe, cart, large_order_promo))
          
            其实只要是支持高阶函数的语言,就可以如此实现,例如 C
            
              #
            
            
               中,可以用委托实现。只是如此实现反而使代码变得复杂不易懂。而 Python 中,函数天然就可以当做参数来传递。
            
          

值得注意的是,《设计模式:可复用面向对象软件的基础》一书的作者指出:“策略对象通常是很好的享元。”  享元是可共享的对象,可以同时在多个上下文中使用。共享是推荐的做法,这样不必在每个新的上下文(这里是 Order 实例)中使用相同的策略时不断新建具体策略对象,从而减少消耗。因此,为了避免 [策略模式] 的运行时消耗,可以配合 [享元模式] 一起使用,但这样,代码行数和维护成本会不断攀升。

在复杂的情况下,需要具体策略维护内部状态时,可能需要把“策略”和“享元”模式结合起来。但是,具体策略一般没有内部状态,只是处理上下文中的数据。此时,一定要使用普通的函数,别去编写只有一个方法的类,再去实现另一个类声明的单函数接口。函数比用户定义的类的实例轻量,而且无需使用“享元”模式,因为各个策略函数在 Python 编译模块时只会创建一次。普通的函数也是“可共享的对象,可以同时在多个上下文中使用”。

 


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论