漫谈设计模式 [13]:命令模式

引导性开场

菜鸟:老鸟,我最近在做一个项目,需要实现一些复杂的操作,比如撤销、重做功能,感觉代码越来越混乱了。你能不能帮我看看有没有什么好的设计模式可以用?

老鸟:嗯,听起来你遇到了典型的命令模式(Command Pattern)适用场景。你知道什么是命令模式吗?

菜鸟:我听说过,但不太明白具体是怎么回事。

渐进式介绍概念

老鸟:好,那我们从生活中的例子开始吧。想象一下,你在一家餐馆点餐。你给服务员下单,服务员把你的订单传递给厨房,厨师根据订单准备食物。这里,菜单上的每一道菜就像一个"命令",服务员是"调用者",厨师是"接收者"。命令模式就是把这种模式应用到代码中。

菜鸟:哦!这样说好像有点明白了。

老鸟:简单来说,命令模式将请求封装成对象,从而让你可以用不同的请求、队列或者日志来参数化其他对象。这样可以更清晰地管理请求。

Python代码示例,逐步展开

菜鸟:能不能用代码给我演示一下?

老鸟:当然可以。我们先从一个简单的例子开始。假设我们有一个灯(Light)类,可以打开和关闭灯:

python 复制代码
class Light:
    def turn_on(self):
        print("The light is on")
    
    def turn_off(self):
        print("The light is off")

老鸟:现在,我们用命令模式来控制这个灯。首先,我们创建一个命令接口(Command):

python 复制代码
from abc import ABC, abstractmethod

class Command(ABC):
    @abstractmethod
    def execute(self):
        pass

class TurnOnLightCommand(Command):
    def __init__(self, light: Light):
        self._light = light
    
    def execute(self):
        self._light.turn_on()

class TurnOffLightCommand(Command):
    def __init__(self, light: Light):
        self._light = light
    
    def execute(self):
        self._light.turn_off()

菜鸟:我明白了。这样我们就把具体的操作封装成命令了。

老鸟:没错。接下来,我们需要一个调用者(Invoker)来执行这些命令:

python 复制代码
class RemoteControl:
    def __init__(self):
        self._commands = []
    
    def submit(self, command: Command):
        self._commands.append(command)
        command.execute()

老鸟:现在我们可以用命令模式来控制灯了:

python 复制代码
light = Light()
turn_on = TurnOnLightCommand(light)
turn_off = TurnOffLightCommand(light)

remote = RemoteControl()
remote.submit(turn_on)  # 输出: The light is on
remote.submit(turn_off)  # 输出: The light is off

问题与反思

菜鸟 :这看起来不错,但如果我直接在 RemoteControl 里调用 Light 的方法,不是更简单吗?

老鸟 :是的,直接调用确实更简单。但是,命令模式的优势在于它的灵活性和可扩展性。例如,如果你想添加撤销功能,只需在 Command 接口中添加一个 undo 方法,并在具体命令类中实现它。

python 复制代码
class Command(ABC):
    @abstractmethod
    def execute(self):
        pass
    
    @abstractmethod
    def undo(self):
        pass

class TurnOnLightCommand(Command):
    def __init__(self, light: Light):
        self._light = light
    
    def execute(self):
        self._light.turn_on()
    
    def undo(self):
        self._light.turn_off()

class TurnOffLightCommand(Command):
    def __init__(self, light: Light):
        self._light = light
    
    def execute(self):
        self._light.turn_off()
    
    def undo(self):
        self._light.turn_on()

老鸟 :然后我们在 RemoteControl 中添加一个 undo 方法:

python 复制代码
class RemoteControl:
    def __init__(self):
        self._commands = []
    
    def submit(self, command: Command):
        self._commands.append(command)
        command.execute()
    
    def undo(self):
        if self._commands:
            last_command = self._commands.pop()
            last_command.undo()

老鸟:现在你可以撤销最后的命令了:

python 复制代码
remote.submit(turn_on)  # 输出: The light is on
remote.undo()           # 输出: The light is off

优势与适用场景

菜鸟:原来如此。命令模式确实很灵活,特别是在需要撤销操作的时候。

老鸟:对,命令模式的优势还在于它能够将对象行为参数化、记录日志、支持事务性操作等。适用的场景包括:

  • 需要对行为进行抽象和参数化。
  • 需要撤销、重做操作。
  • 需要记录操作日志。
  • 需要实现事务性操作。

常见误区与优化建议

菜鸟:这种模式会不会让代码变得很复杂?

老鸟:确实,如果过度使用命令模式,可能会导致代码复杂度增高。关键在于平衡,只有在真正需要灵活性和可扩展性的时候才使用它。另外,要避免将太多逻辑放在命令类中,保持类的单一职责。

总结与延伸阅读

老鸟:总结一下,命令模式将请求封装成对象,从而使你可以用不同的请求来参数化其他对象。它的优势在于灵活性和可扩展性,适用于需要撤销、重做、记录日志等操作的场景。

老鸟:如果你对设计模式感兴趣,我推荐你继续学习其他模式,比如策略模式、观察者模式等。相关书籍如《设计模式:可复用面向对象软件的基础》和《Head First 设计模式》都很不错。

菜鸟:谢谢老鸟,我会继续学习的!

相关推荐
玄同76521 小时前
LangChain 1.0 模型接口:多厂商集成与统一调用
开发语言·人工智能·python·langchain·知识图谱·rag·智能体
喵手21 小时前
Python爬虫实战:构建招聘会数据采集系统 - requests+lxml 实战企业名单爬取与智能分析!
爬虫·python·爬虫实战·requests·lxml·零基础python爬虫教学·招聘会数据采集
专注VB编程开发20年21 小时前
python图片验证码识别selenium爬虫--超级鹰实现自动登录,滑块,点击
数据库·python·mysql
iFeng的小屋1 天前
【2026最新当当网爬虫分享】用Python爬取千本日本相关图书,自动分析价格分布!
开发语言·爬虫·python
民乐团扒谱机1 天前
【微科普】3D 演奏蠕虫分析图:解码音乐表演情感的 “可视化语言”
python·可视化·音乐·3d图·3d蠕虫
芝士爱知识a1 天前
AlphaGBM 深度解析:下一代基于 AI 与蒙特卡洛的智能期权分析平台
数据结构·人工智能·python·股票·alphagbm·ai 驱动的智能期权分析·期权
52Hz1181 天前
力扣230.二叉搜索树中第k小的元素、199.二叉树的右视图、114.二叉树展开为链表
python·算法·leetcode
喵手1 天前
Python爬虫实战:网页截图归档完全指南 - 构建生产级页面存证与历史回溯系统!
爬虫·python·爬虫实战·零基础python爬虫教学·网页截图归档·历史回溯·生产级方案
张3蜂1 天前
Python 四大 Web 框架对比解析:FastAPI、Django、Flask 与 Tornado
前端·python·fastapi