Python设计模式之策略模式(15)

策略模式(The Strategy Pattern): 动态选择算法策略。它能让你定义一系列算法, 并将每种算法分别放入独立的类中, 以使算法的对象能够相互替换。
在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。

1 介绍

现实中的例子:
大多数问题都可以使用多种方法来解决。以排序问题为例,对于以一定次序把元素放入一个列表,排序算法有很多。通常来说,没有公认最适合所有场景的算法(。一些不同的评判标准能帮助我们为不同的场景选择不同的排序算法,其中应该考虑的有以下几个。

  • 需要排序的元素数量:这被称为输入大小。当输入较少时,几乎所有排序算法的表现都很好,但对于大量输入,只有部分算法具有不错的性能。
  • 算法的最佳/平均/最差时间复杂度:时间复杂度是算法运行完成所花费的(大致)时间长短,不考虑系数和低阶项(在算法分析中,只考虑时间复杂度函数的最高次项,不考虑低阶项,也忽略最高次项的系数)。这是选择算法的最常见标准,但这个标准并不总是那么充分。
  • 算法的空间复杂度:空间复杂度是充分地运行一个算法所需要的(大致)物理内存量。在我们处理大数据或在嵌入式系统(通常内存有限)中工作时,这个因素非常重要。
  • 算法的稳定性:在执行一个排序算法之后,如果能保持相等值元素原来的先后相对次序,则认为它是稳定的。
  • 算法的代码实现复杂度:如果两个算法具有相同的时间/空间复杂度,并且都是稳定的,那么知道哪个算法更易于编码实现和维护也是很重要的。

策略模式(Strategy pattern)鼓励使用多种算法来解决一个问题,其杀手级特性是能够在运行时透明地切换算法(客户端代码对变化尤感知)。因此,如果你有两种算法,并且知道其中一种对少量输入效果更好,另一种对大量输入效果更好,则可以使用策略模式在运行时基于输入数据决定使用哪种算法。

  • 意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
  • 主要解决:在有多种算法相似的情况下,使用 if…else 所带来的复杂和难以维护。
  • 何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。
  • 如何解决:将这些算法封装成一个一个的类,任意地替换。
  • 关键代码:实现同一个接口。
  • 优点:
    你可以在运行时切换对象内的算法。
    你可以将算法的实现和使用算法的代码隔离开来。
    你可以使用组合来代替继承。
    你无需对上下文进行修改就能够引入新的策略。
  • 缺点:
    如果你的算法极少发生改变, 那么没有任何理由引入新的类和接口。 使用该模式只会让程序过于复杂。
    客户端必须知晓策略间的不同——它需要选择合适的策略。
    许多现代编程语言支持函数类型功能, 允许你在一组匿名函数中实现不同版本的算法。 这样, 你使用这些函数的方式就和使用策略对象时完全相同, 无需借助额外的类和接口来保持代码简洁。
  • 注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。

2 适用场景

当你想使用对象中各种不同的算法变体, 并希望能在运行时切换算法时, 可使用策略模式。
策略模式让你能够将对象关联至可以不同方式执行特定子任务的不同子对象, 从而以间接方式在运行时更改对象行为。
当你有许多仅在执行某些行为时略有不同的相似类时, 可使用策略模式。
策略模式让你能将不同行为抽取到一个独立类层次结构中, 并将原始类组合成同一个, 从而减少重复代码。
如果算法在逻辑的上下文中不是特别重要, 使用该模式能将类的业务逻辑与其算法实现细节隔离开来。
策略模式让你能将各种算法的代码、 内部数据和依赖关系与其他代码隔离开来。 不同客户端可通过一个简单接口执行算法, 并能在运行时进行切换。
当类中使用了复杂条件运算符以在同一算法的不同变体中切换时, 可使用该模式。
策略模式将所有继承自同样接口的算法抽取到独立类中, 因此不再需要条件语句。 原始对象并不实现所有算法的变体, 而是将执行工作委派给其中的一个独立算法对象。

3 使用步骤

策略模式结构
在这里插入图片描述

实现方式

  • 从上下文类中找出修改频率较高的算法 (也可能是用于在运行时选择某个算法变体的复杂条件运算符)。
  • 声明该算法所有变体的通用策略接口。
  • 将算法逐一抽取到各自的类中, 它们都必须实现策略接口。
  • 在上下文类中添加一个成员变量用于保存对于策略对象的引用。 然后提供设置器以修改该成员变量。 上下文仅可通过策略接口同策略对象进行交互, 如有需要还可定义一个接口来让策略访问其数据。
  • 客户端必须将上下文类与相应策略进行关联, 使上下文可以预期的方式完成其主要工作。

步骤

  • 策略接口声明了某个算法各个不同版本间所共有的操作。上下文会使用该接口来调用有具体策略定义的算法。
  • 具体策略会在遵循策略基础接口的情况下实现算法。该接口实现了它们在上下文
    中的互换性。
  • 上下文定义客户端关注的接口,维护指向某个策略对象的引用。同时还提供设置器以便在运行时切换策略。上下文会将一些工作委派给策略对象,而不是自行实现不同版本的算法。
  • 客户端代码会选择具体策略并将其传递给上下文。客户端必须知晓策略之间的差异,才能做出正确的选择。

4 代码示例

概念示例

from abc import ABC, abstractmethod
from typing import List

class Stragegy(ABC):
    """策略接口声明了某个算法各个不同版本间所共有的操作。上下文会使用该接口来调用有具体策略定义的算法。"""
    def altorithm(self
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值