实战设计模式之命令模式

概述

命令模式是一种行为设计模式,用于将请求封装成对象,从而使我们可以用不同的请求对客户进行参数化。简单来说,命令模式就是把一个请求转换为一个包含所有关于这个请求信息的对象。这样,就可以像传递其他对象一样传递这个请求,并且可以在适当的时间和地点执行它。

命令模式主要适用于以下几种应用场景。

1、需要支持撤销、重做操作的应用程序,比如:文本编辑器、绘图程序等。

2、操作可以被放入队列中,并按顺序或特定条件执行的任务调度系统。

3、提供统一接口给用户执行一系列操作的地方,比如:菜单项、工具栏等UI组件。

线上购物平台的订单处理是运用命令模式的一个典型例子:在线购物平台接受用户的购买请求时,实际上是在创建订单命令。这些命令包含所有必要的信息,包括:商品详情、数量、配送地址等。一旦用户确认下单,系统就会处理这个命令,包括:库存检查、价格计算、支付验证等一系列步骤。如果出现问题,还可以取消命令,类似于撤销操作。

基本原理

命令模式的核心思想是:将"动作"和"执行动作的对象"解耦开来,通过引入一个中间层(即命令对象)来间接地调用接收者的业务方法。命令模式主要由以下五个核心组件构成。

1、命令接口。定义了一个Execute方法,有时还会有Undo等方法,所有具体命令类都将实现这个接口。

2、具体命令。实现了命令接口,并绑定了一个接收者和动作。当调用Execute方法时,它会调用接收者的相应操作。如果需要撤销功能,也会实现Undo方法。

3、接收者。包含了真正执行业务逻辑的方法,这些方法是由具体命令类在执行时调用的。

4、调用者。负责调用命令对象的Execute方法,它可以持有一个或多个命令,并根据需要触发它们。

5、客户端。创建具体的命令对象,并设置好接收者,之后可以将命令交给调用者。

基于上面的核心组件,命令模式的实现主要有以下五个步骤。

1、定义命令接口。定义一个命令接口,至少包含一个Execute方法,这为所有的具体命令提供了一致的行为。

2、创建接收者类。创建接收者类,该类包含了实际执行业务逻辑的方法。

3、实现具体命令类。为每个可能的动作实现具体命令类,这些类实现了命令接口,并且通常会在构造函数中接受一个接收者实例。

4、设计调用者。调用者类可以持有并管理一个或多个命令对象,并在适当的时候调用命令的Execute方法。

5、编写客户端代码。在客户端代码中,创建接收者、具体命令以及调用者,并将命令与接收者关联起来。最后,可以通过调用者来执行命令。

实战解析

在下面的实战代码中,我们使用命令模式模拟了线上购物平台的订单处理。

首先,我们定义了接收者类COrderService。它包含了实际执行业务逻辑的方法,比如:创建订单、取消订单和支付订单,这些方法是具体操作的实现。

接着,我们定义了一个命令接口CCommand,它声明了一个纯虚函数Execute。所有具体的命令类都将继承这个接口并实现该方法,确保每个命令都有一个统一的执行入口。

然后,我们实现了三个具体命令类。CCreateOrderCommand封装了创建订单的请求,CCancelOrderCommand封装了取消订单的请求,CPayOrderCommand封装了支付订单的请求。每个具体命令类在构造时接收一个COrderService指针和其他必要参数,并在其Execute方法中调用COrderService相应的方法来完成实际操作。

我们还定义了调用者类CUserInterface,它负责管理命令对象,并提供添加命令和处理命令的方法。AddCommand函数用于向命令队列中添加新的命令对象。ProcessCommands函数遍历命令队列,依次调用每个命令的Execute方法以执行命令,并在执行后删除命令对象以释放资源。

最后,在main函数中,我们创建了COrderService实例作为接收者,创建了CUserInterface实例作为调用者。为了模拟用户操作,我们通过调用者的AddCommand方法将创建订单、支付订单和取消订单的具体命令加入到命令队列中,并调用ProcessCommands方法来依次执行所有命令。

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>

using namespace std;

// 接收者:执行实际的业务逻辑
class COrderService
{
public:
    void CreateOrder(const string& strDetails)
    {
        cout << "Create order with details: " << strDetails << endl;
    }

    void CancelOrder(int nOrderId)
    {
        cout << "Cancel order ID: " << nOrderId << endl;
    }

    void PayForOrder(int nOrderId)
    {
        cout << "Pay for order ID: " << nOrderId << endl;
    }
};

// 命令接口
class CCommand
{
public:
    virtual ~CCommand() {}
    virtual void Execute() = 0;
};

// 具体命令:创建订单命令
class CCreateOrderCommand : public CCommand
{
public:
    CCreateOrderCommand(COrderService* pService, const string& strDetails) : 
        m_pService(pService), m_strDetails(strDetails) {}

    void Execute()
    {
        m_pService->CreateOrder(m_strDetails);
    }

private:
    COrderService* m_pService;
    string m_strDetails;
};

// 具体命令:取消订单命令
class CCancelOrderCommand : public CCommand
{
public:
    CCancelOrderCommand(COrderService* pService, int nOrderId) : 
        m_pService(pService), m_nOrderId(nOrderId) {}

    void Execute()
    {
        m_pService->CancelOrder(m_nOrderId);
    }

private:
    COrderService* m_pService;
    int m_nOrderId;
};

// 具体命令:支付订单命令
class CPayOrderCommand : public CCommand
{
public:
    CPayOrderCommand(COrderService* pService, int nOrderId) : 
        m_pService(pService), m_nOrderId(nOrderId) {}

    void Execute()
    {
        m_pService->PayForOrder(m_nOrderId);
    }

private:
    COrderService* m_pService;
    int m_nOrderId;
};

// 调用者:用户界面或API端点
class CUserInterface
{
public:
    ~CUserInterface()
    {
        vector<CCommand*>::iterator it = m_vctCommand.begin();
        while (it != m_vctCommand.end())
        {
            CCommand *pCommand = *it;
            delete pCommand;
            it++;
        }

        m_vctCommand.clear();
    }

    void AddCommand(CCommand* pCommand)
    {
        m_vctCommand.push_back(pCommand);
    }

    void ProcessCommands()
    {
        vector<CCommand*>::iterator it = m_vctCommand.begin();
        while (it != m_vctCommand.end())
        {
            CCommand *pCommand = *it;
            pCommand->Execute();
            delete pCommand;
            it++;
        }

        m_vctCommand.clear();
    }

private:
    vector<CCommand*> m_vctCommand;
};

int main()
{
    COrderService orderService;

    // 创建用户界面
    CUserInterface ui;

    // 模拟用户操作
    ui.AddCommand(new CCreateOrderCommand(&orderService, "6 items"));
    ui.AddCommand(new CPayOrderCommand(&orderService, 88888));
    ui.AddCommand(new CCancelOrderCommand(&orderService, 88888));

    // 处理所有命令
    ui.ProcessCommands();
    return 0;
}

总结

命令模式使得发起命令的一方不需要知道谁会处理该命令,也不需要知道命令的具体细节,只需要知道如何触发命令即可。通过命令模式,我们可以轻松地实现撤销和重做功能,只需保存命令的历史记录,并在需要时再次执行或反向执行这些命令。另外,新增加命令类型不会影响现有的命令处理流程,符合开闭原则,即对扩展开放,对修改关闭。

但如果命令对象数量庞大,且每个命令都需要保存状态信息,则可能会占用较多的内存资源。当命令之间存在复杂的依赖关系时,维护和管理这些关系可能会变得困难,比如:当一个命令的执行依赖于另一个命令的结果时。

相关推荐
vker1 小时前
第 1 天:单例模式(Singleton Pattern)—— 创建型模式
java·设计模式
晨米酱19 小时前
JavaScript 中"对象即函数"设计模式
前端·设计模式
数据智能老司机1 天前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机1 天前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机1 天前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机1 天前
精通 Python 设计模式——性能模式
python·设计模式·架构
使一颗心免于哀伤1 天前
《设计模式之禅》笔记摘录 - 21.状态模式
笔记·设计模式
数据智能老司机2 天前
精通 Python 设计模式——创建型设计模式
python·设计模式·架构
数据智能老司机2 天前
精通 Python 设计模式——SOLID 原则
python·设计模式·架构
烛阴2 天前
【TS 设计模式完全指南】懒加载、缓存与权限控制:代理模式在 TypeScript 中的三大妙用
javascript·设计模式·typescript