logo

该视频仅会员有权观看

立即开通课程「Python 入门」权限。

¥
199
/ 年

策略模式(实战)

上面我们通过工厂模式来扩展了系统的功能,其实还有一部分代码值得我们关注,那就是任务执行模块,我们来看下这部分代码:

# engine.py def _execute_task(self, host: Host, task: Task): """执行单个任务""" if task.module: # 执行模块 module_class = self.plugins.get(task.module) # 获取模块 if module_class: # 如果模块存在 module = module_class() # 创建模块实例 task.result = module.execute(host, *task.args) # 执行模块 else: raise ValueError(f"Module {task.module} not found") else: # 执行命令 task.result = host.execute_command(task.command) # 执行任务 task.status = "success" if task.result[ "exit_status"] == 0 else "failed" # 设置任务状态 return host, task

这段代码中,我们通过 if-else 来判断是执行模块还是执行命令,这里代码就有点重复了,如果我们要添加新的任务类型,比如 shell 任务,那么我们就需要去修改这部分代码,这样会导致代码变得臃肿,可维护性降低,那么有什么方法可以让我们在执行任务的时候,不需要去关心是执行模块还是执行命令,只需要关心执行任务的接口是什么就行了呢?

对的,我们可以使用一种设计模式叫做 策略模式 来优化这部分代码,策略模式是一种行为设计模式,它定义了一系列算法,将每个算法封装起来,并使它们可以互相替换。让我详细介绍一下策略模式:

  1. 定义: 策略模式允许你定义一系列算法,把它们封装起来,并且使它们可以互相替换。该模式让算法的变化独立于使用算法的客户端。

  2. 主要组成部分:

    • 上下文(Context): 维护一个对策略对象的引用,可以定义一个接口来让策略访问它的数据。
    • 策略(Strategy): 定义所有支持的算法的公共接口。
    • 具体策略(Concrete Strategies): 实现了策略接口的具体算法。
  3. 策略模式的优点:

    • 算法可以自由切换
    • 避免使用多重条件判断
    • 扩展性良好,符合开闭原则
    • 策略算法的复用性更高
  4. 使用场景:

    • 当你有许多类似的类,它们仅在某些行为上不同时
    • 当你需要使用一个算法的不同变体时
    • 当你想要在运行时选择算法时
    • 当一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现时
  5. 实现示例:

from abc import ABC, abstractmethod # 策略接口 class PaymentStrategy(ABC): @abstractmethod def pay(self, amount): pass # 具体策略 class CreditCardPayment(PaymentStrategy): def pay(self, amount): print(f"使用信用卡支付{amount}元") class AlipayPayment(PaymentStrategy): def pay(self, amount): print(f"使用支付宝支付{amount}元") class WeChatPayment(PaymentStrategy): def pay(self, amount): print(f"使用微信支付{amount}元") # 上下文 class ShoppingCart: def __init__(self): self.items = [] self.payment_strategy = None def add_item(self, item): self.items.append(item) def set_payment_strategy(self, payment_strategy): self.payment_strategy = payment_strategy def checkout(self): total = sum(item.price for item in self.items) if self.payment_strategy: self.payment_strategy.pay(total) else: print("请先设置支付方式") # 使用示例 class Item: def __init__(self, name, price): self.name = name self.price = price # 客户端代码 cart = ShoppingCart() cart.add_item(Item("书", 50)) cart.add_item(Item("笔", 10)) cart.set_payment_strategy(CreditCardPayment()) cart.checkout() # 输出: 使用信用卡支付60元 cart.set_payment_strategy(AlipayPayment()) cart.checkout() # 输出: 使用支付宝支付60元

策略模式在许多场景中都很有用,比如排序算法、验证策略、支付方式等。它提供了一种灵活的方式来管理一组相关的算法,使得算法可以独立于使用它的客户端而变化。

现在回到我们之前讨论的任务执行系统中,策略模式允许我们为不同类型的任务(如命令执行、模块执行、长时间运行任务等)定义不同的执行策略,而不需要在执行引擎中使用大量的条件语句。这使得系统更容易扩展和维护,接下来我们就可以使用策略模式来优化我们的任务执行系统了。

首先创建一个 strategy.py 文件,然后添加一个 TaskStrategy 抽象基类,代码如下:

# strategy.py from abc import ABC, abstractmethod from host import Host from task import Task class TaskStrategy(ABC): @abstractmethod def execute(self, host: Host, task: Task): pass

这是所有策略的基类,它定义了一个抽象方法 execute,所有具体的策略类都必须实现这个方法。

然后就可以创建具体的策略类了,比如 CommandTaskStrategy 类,代码如下:

# strategy.py class CommandTaskStrategy(TaskStrategy): def execute(self, host: Host, task: Task): task.result = host.execute_command(task.command) task.status = "success" if task.result[ "exit_status"] == 0 else "failed" return host, task

这个策略用于执行简单的命令,它直接调用主机的 execute_command 方法来执行任务中定义的命令。

同样的方式继续定义模块执行策略 ModuleTaskStrategy 类,代码如下:

# strategy.py class ModuleTaskStrategy(TaskStrategy): def __init__(self, plugins): self.plugins = plugins def execute(self, host: Host, task: Task): module_class = self.plugins.get(task.module) if module_class: module = module_class() task.result = module.execute(host, *task.args) else: raise ValueError(f"Module {task.module} not found") return host, task

这个策略用于执行模块任务,它首先从插件列表中获取对应的模块类,然后创建模块实例并执行。

接下来我们就可以在 engine.py 文件中使用策略模式来执行任务了,代码如下:

# engine.py class ExecutionEngine: # ...... 省略部分代码 ...... def _execute_task(self, host: Host, task: Task): """执行单个任务""" strategy = self._get_task_strategy(task) return strategy.execute(host, task) def _get_task_strategy(self, task: Task): """获取任务策略""" if task.module: return ModuleTaskStrategy(self.plugins) elif task.command: return CommandTaskStrategy() else: raise ValueError("Task has neither command nor module")

上面代码中我们根据任务的类型来选择对应的策略,从而执行不同的任务,这样我们就不需要去关心是执行模块还是执行命令,只需要关心执行任务的接口是什么就行,这样我们就可以很方便的扩展新的任务类型了。比如还有其他任务类型,比如 long_running_task 任务,那么我们就可以创建一个 LongRunningTaskStrategy 类,然后去实现 execute 方法即可,对于执行引擎中执行任务部分的代码非常简洁,而且非常容易扩展。