0. 简介

我们在遇到类似GUI中多种Button点击功能的操作时,我们应该将请求的所有细节 (例如调用的对象、 方法名称和参数列表) 抽取出来组成命令类, 该类中仅包含一个用于触发请求的方法。

命令模式就是这样一种行为设计模式, 它可将请求转换为一个包含与请求相关的所有信息的独立对象。 并能根据不同的请求将方法参数化、 延迟请求执行或将其放入队列中, 且能实现可撤销操作。

1. 命令模式示意图

命令模式可将特定的方法调用转化为独立对象。 这一改变也带来了许多有趣的应用: 你可以将命令作为方法的参数进行传递、 将命令保存在其他对象中, 或者在运行时切换已连接的命令等。

在这里插入图片描述

  1. Command(抽象命令类):抽象出命令对象,可以根据不同的命令类型。写出不同的实现类

  2. Concrete Command(具体命令类):实现了抽象命令对象的具体实现

  3. Invoker(调用者/请求者):请求的发送者,它通过命令对象来执行请求。一个调用者并不需要在设计时确定其接收者,因此它只与抽象命令来之间存在关联。在程序运行时,将调用命令对象的execute() ,间接调用接收者的相关操作。

  4. Receiver(接收者):接收者执行与请求相关的操作,真正执行命令的对象。具体实现对请求的业务处理。未抽象前,实际执行操作内容的对象。

  5. Client(客户端):在客户类中需要创建调用者对象,具体命令类对象,在创建具体命令对象时指定对应的接收者。发送者和接收者之间没有之间关系。都通过命令对象来调用。

2. 示例程序

责任链模式和命令模式用于处理请求发送者和接收者之间的不同连接方式。责任链按照顺序将请求动态传递给一系列的潜在接收者, 直至其中一名接收者对请求进行处理。命令模式则是在发送者和请求者之间建立单向连接
作为命令模式的作用,非常明显,它可以通过某些行为来参数化对象。从而避免了继承导致的调用与接收的紧耦合。命令模式则是分离了这两者,修改时只需要添加接收者并创建新的实例化具体命令类即可实现整套代码的调用
在这里插入图片描述

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>

using namespace std;

// 接受者,作为最底层的调用
class Receiver
{
public:
        void BakeMutton()
        {
                cout<< "烤羊肉"<< endl;
        }

        void BakeChicken()
        {
                cout<< "烤鸡翅"<< endl;
        }
};

// 基类
class Command
{
public:
        Command(Receiver* pstReceiver):m_pstReceiver(pstReceiver)
        {

        }
        virtual void Excute() = 0;

protected:
        Receiver* m_pstReceiver;
};

// 具体类,用于调用接收者
class ConcreteCommandA: public Command
{
public:
        ConcreteCommandA(Receiver* pstReceiver):Command(pstReceiver)
        {

        }
        virtual void Excute()
        {
                cout<< "ConcreteCommandA excuting......"<< endl;
                m_pstReceiver->BakeMutton();
        }

};

// 具体类,用于调用接收者
class ConcreteCommandB: public Command
{
public:
        ConcreteCommandB(Receiver* pstReceiver):Command(pstReceiver)
        {

        }
        virtual void Excute()
        {
                cout<< "ConcreteCommandB excuting......"<< endl;
                m_pstReceiver->BakeChicken();
        }
};

// 调用者,作为最上层,用于管理具体类的操作,从而对接收者增删
class Invoke
{
public:
        void Add(Command* pstCommand)
        {
                m_vecPstCommand.push_back(pstCommand);
        }
        void Remove(Command* pstCommand)
        {
                m_vecPstCommand.erase(find(m_vecPstCommand.begin(), m_vecPstCommand.end(), pstCommand));
        }
        void RemoveAll()
        {
                m_vecPstCommand.clear();
        }
        void Notify()
        {
                for (typeof(m_vecPstCommand.begin()) it = m_vecPstCommand.begin(); it != m_vecPstCommand.end(); ++it)
                {
                        (*it)->Excute();
                }
        }

private:
        vector<Command*> m_vecPstCommand;
};

int main(int argc, char* argv[])
{
        Receiver* pstReceiver = new Receiver();
        Command* pstConcreteCommandA = new ConcreteCommandA(pstReceiver);
        Command* pstConcreteCommandB = new ConcreteCommandB(pstReceiver);
        Invoke* pstInvoke = new Invoke();

        pstInvoke->Add(pstConcreteCommandA);
        pstInvoke->Add(pstConcreteCommandA);
        pstInvoke->Add(pstConcreteCommandB);
        pstInvoke->Notify();
        cout<< "------------------"<< endl<< endl;

        pstInvoke->Remove(pstConcreteCommandA);  //撤销操作
        pstInvoke->Remove(pstConcreteCommandB);
        pstInvoke->Notify();
        cout<< "------------------"<< endl<< endl;

        return 0;
}

/**
ConcreteCommandA excuting......
烤羊肉
ConcreteCommandA excuting......
烤羊肉
ConcreteCommandB excuting......
烤鸡翅
------------------

ConcreteCommandA excuting......
烤羊肉
------------------ 
**/

3. 命令模式的优缺点

下面是命令模式的优缺点,总结一下,命令模式最大的优点在于:类间解耦,调用者和接收者没有直接耦合,调用者只需调用Command的方法执行接收者的业务逻辑;可扩展性,如果Command需要扩展,很容易实现,新建一个新的子类即可。缺点就是:如果命令很多,Command类会很多,增加系统复杂性。
在这里插入图片描述

4. 参考链接

https://refactoringguru.cn/design-patterns/command

https://www.cnblogs.com/hebaichuanyeah/p/5713031.html

https://www.cnblogs.com/meet/p/5116430.html