Skip to main content

2、策略模式

Y-aong...About 4 minpython策略模式设计模式

2、策略模式

一、定义

策略模式(Strategy Pattern)是行为设计模式的一种,它定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。在Python中实现策略模式通常涉及创建一个接口或基类,以及多个实现了该接口或继承自该基类的具体策略类。

二、策略模式结构

  1. 上下文 (Context) 维护指向具体策略的引用, 且仅通过策略接口与该对象进行交流。
  2. 策略 (Strategy) 接口是所有具体策略的通用接口, 它声明了一个上下文用于执行策略的方法。
  3. 具体策略 (Concrete Strategies) 实现了上下文所用算法的各种不同变体。

策略模式是最常用的一种设计模式

from __future__ import annotations
from abc import ABC, abstractmethod
from typing import List


class Context(object):
    """上下文"""

    def __init__(self, strategy: Strategy) -> None:
        self._strategy = strategy

    @property
    def strategy(self) -> Strategy:
        return self._strategy

    @strategy.setter
    def strategy(self, strategy: Strategy) -> None:
        self._strategy = strategy

    def do_some_business_logic(self) -> None:
        print("Context: Sorting data using the strategy (not sure how it'll do it)")
        result = self._strategy.do_algorithm(["a", "b", "c", "d", "e"])
        print(",".join(result))


class Strategy(ABC):
    """策略接口"""

    @abstractmethod
    def do_algorithm(self, data: List):
        pass


class ConcreteStrategyA(Strategy):
    # 具体策略A
    def do_algorithm(self, data: List) -> List:
        return sorted(data)


class ConcreteStrategyB(Strategy):
    # 具体策略B
    def do_algorithm(self, data: List) -> List:
        return reversed(sorted(data))


if __name__ == "__main__":
    context = Context(ConcreteStrategyA())
    print("Client: Strategy is set to normal sorting.")
    context.do_some_business_logic()
    print()

    print("Client: Strategy is set to reverse sorting.")
    context.strategy = ConcreteStrategyB()
    context.do_some_business_logic()

《流畅的Python》一书中关于策略模式的说明非常详细,它不仅展示了如何使用面向对象的方式实现策略模式,还介绍了如何利用Python的一等函数特性来简化这种设计模式的实现。

由于Python中的函数是一等公民,可以像其他任何值一样被传递、赋值或作为参数传递给其他函数,因此可以直接用函数来代替类来实现具体策略

# 策略模式(函数实现)
 
import inspect
import promotions
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):
    """ 为积分为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
 
 
# promos = [fidelity_promo, bulk_item_promo, large_order_promo]
# promos = [globals()[name] for name in globals() if name.endswith("_promo")]
promos = [func for name, func in inspect.getmembers(promotions, inspect.isfunction)]
 
 
def best_promo(order):
    """ 选择可用的最佳折扣 """
    return max(promo(order) for promo in promos)
 
 
if __name__ == '__main__':
    joe = Customer('John Doe', 0)
    ann = Customer("Ann Smith", 1100)
    cart = [LineItem("banana", 4, .5),
            LineItem("apple", 10, 1.5),
            LineItem("watermellon", 5, 5.0)]
    print(Order(joe, cart, fidelity_promo))
    print(Order(ann, cart, fidelity_promo))
 
    banana_cart = [LineItem("banana", 30, .5),
                   LineItem("apple", 10, 1.5)]
    print(Order(joe, banana_cart, bulk_item_promo))
 
    long_cart = [LineItem(str(item_code), 1, 1.0) for item_code in range(10)]
    print(Order(joe, long_cart, large_order_promo))
    print(Order(joe, cart, large_order_promo))
 
    print(Order(joe, long_cart, best_promo))
    print(Order(joe, banana_cart, best_promo))
    print(Order(ann, cart, best_promo))

同时我在工作中也会这么使用。即通过将算法或行为封装起来,使得它们可以在运行时根据需要选择和替换。然而,这段代码更接近于一个简单的查找表或者说是基于函数的一等公民特性实现的轻量级策略模式,我觉得他也体现了策略模式的思路。即在这个例子中,Python 的一等函数特性被用来直接将函数作为策略对象,从而避免了为每个新策略创建额外的类所带来的复杂性。这种方式简化了代码结构,并且更加符合 Pythonic 的风格。

def handle_week():
    print('处理一周数据')


def handle_day():
    print('处理一天数据')


def handle_month():
    print('处理一月数据')
  

handlers = {
    'week': handle_week,
    'day': handle_day,
    'month': handle_month
}

在传统的面向对象编程中,策略模式通常涉及定义一个接口(或抽象基类),然后创建多个实现了该接口的具体策略类。每个具体策略类都提供了一种特定的行为实现。

但是《流畅的python》一书中还探讨了如何利用Python的一等函数特性来进一步简化策略模式。由于Python中的函数是一等公民,可以像其他任何值一样被传递、赋值或作为参数传递给其他函数,因此可以直接用函数来代替类来实现具体策略。

Comments
  • Latest
  • Oldest
  • Hottest
Powered by Waline v2.15.8