C++设计模式9–命令模式–降低请求发送者接收者耦合

作者: veaxen 分类: 设计模式 发布时间: 2019-02-26 23:19

命令模式概述

在软件系统中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合,比如要对行为“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,实现二者之际的松耦合。这就是命令模式(Command Pattern)

举个显示中的例子,我们的电视与遥控器就是命令模式,拿着遥控器给电视发送红外命令,电视就可以按照收到的命令换台和待机。

命令模式结构

  1. Command
    定义命令的接口,声明执行的方法。
  2. ConcreteCommand
    命令接口实现对象,是“虚”的实现;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。
  3. Receiver
    接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。
  4. Invoker
    要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于是哟个命令对象的入口。
  5. Client
    创建具体的命令对象,并且设置命令对象的接收者。注意这个不是我们常规意义上的客户端,二十组装命令对象和接收者,或者把这个Client称为装配者会更好理解,因为真正使用命令的客户端是从Invoke来出发执行。

运行方式:

  1. Client在C/C++中是主函数或者成员函数,它创建一个ConcreteCommand对象并指定它的Receiver对象
  2. 某个Invoker对象存储该ConcreteCommand对象
  3. 该Invoker通过调用Command对象的Execute操作来提交一个请求。若该命令是可撤销的,ConcreteCommand就在执行Execute操作之前存储当前状态以用于取消命令
  4. ConcreteCommand对象调用它的Receiver的一些操作以执行该请求

基本代码

先看看命令接收者

命令接收者才是命令真正的执行者

// 命令的接收者,是真正执行命令的对象
class Receiver
{
public:
    virtual void Action()
    {
        std::cout << "do something..." << std::endl;
    }
};

命令接口的实现对象,执行一个命令,通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。

// 命令的接口
class Commad
{
public:
    virtual void Execute() = 0;  // 定义了一个执行的方法
};

// 命令接口的实现对象, 执行一个命令
// 通常会持有接收者,并调用接收者的功能来完成命令的执行操作
class ConcreteCommand : public Command
{
public:
    ConcreteCommand(Receiver *receiver)
    {
        this->receiver = receiver;
    }

    void Execute()
    {
        // 通常会转调对象的相应方法,让接收者来真正执行功能
        receiver->Action();
    }
protected:
    Receiver* receiver;
};

请求发送者,要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象

// 要求命令对象执行请求,
// 通常会持有命令对象,可以持有很多的命令对象
class Invoker
{
public :
    void SetCommand(Command *command)
    {
       this->command = command;
    }

    void RunCommand()
    {
        command->Execute();
    }
protected:
    Command *command;

};

最后是客户端,也就是主程序

int main( )
{
    //创建接收者
    Receiver *receiver = new Receiver;

    //创建命令对象,设定它的接收者
    Command *command = new ConcreteCommand(receiver);

    //创建Invoker,把命令对象设置进去

    Invoker *invoker = new Invoker;
    invoker->SetCommand(command);       // invoker发出command命令
    invoker->RunCommand( );             // 然后去执行这个命令

}

好了我们在这里只是简单讲讲,命令模式的基础概念,以及基本代码实现。下面我们将用具体例子实现一下,方便大家理解

命令模式应用实例

还是以开头说的电视机(Receiver)和遥控器(ConcreteCommad)来举例,遥控器上有一些按钮,不同的按钮对应电机的不同操作。

抽象命令角色由一个命令接口来扮演,具有三个具体的命令类实现了抽象命令接口,这个三个具体命令类分别代表三种操作:打开电视、关闭电视和切换频道。

显然,电视遥控器就是一个典型的命令模式应用实例。

#include <iostream>
#include <vector>
using std::cout;
using std::endl;
using std::vector;
using namespace std;


// 接收者对象, 电视才是真正命令的执行者
class Television
{
public :
    void Open( )       // 打开电视
    {
        std::cout <<"打开电视" <<std::endl;
    }

    void Close( )      // 关闭电视
    {
        std::cout <<"关闭电视" <<std::endl;
    }

    void Change( )     // 切换频道
    {
        std::cout <<"更换频道" <<std::endl;
    }
};


class Command
{
public :
    virtual void Execute( ) = 0;         // 执行的接口
};

// 打开电视的命令
class OpenCommand : public Command
{
public :
    OpenCommand(Television *tv)
    {
        m_tv = tv;
    }

    void Execute( )         // 执行的接口
    {
        //std::cout <<"打开电视" <<std::endl;
        m_tv->Open( );
    }
protected :
    Television *m_tv;
};

// 关闭电视的命令
class CloseCommand : public Command
{
public :
    CloseCommand(Television *tv)
    {
        m_tv = tv;
    }
    void Execute( )         // 执行的接口
    {
        //std::cout <<"关闭电视" <<std::endl;
        m_tv->Close( );
    }
protected :
    Television *m_tv;
};

// 更换频道的命令
class ChangeCommand : public Command
{
public :
    ChangeCommand(Television *tv)
    {
        m_tv = tv;
    }
    void Execute( )         // 执行的接口
    {
        //std::cout <<"更换频道" <<std::endl;
        m_tv->Change( );
    }
protected :
    Television *m_tv;
};

// Invoker请求发送者
// 要求命令对象执行请求,
// 通常会持有命令对象,可以持有很多的命令对象
class Control
{
public :
    void SetCommand(Command *command)
    {
       this->m_command = command;
    }

    void RunCommand( )
    {
        m_command->Execute();
    }

protected:
    Command *m_command;

};

// 主程序其实就相当于我们人
int main( )
{
    // 我们手里有一个遥控器
    Control *control = new Control;

    // 家里有一个大彩电
    Television *tv = new Television;

    /// 首先我们打开电视机
    // 创建一个打开的命令, 这个命令只操纵tv这个对象
    Command *openCommand = new OpenCommand(tv);
    control->SetCommand(openCommand);
    control->RunCommand( );
    delete openCommand;


    // 换了好久频道也没找到好看的, 郁闷的关掉电视机
    // 创建一个更换频道的命令,这个命令只操纵tv这个对象
    Command *changeCommand = new ChangeCommand(tv);
    control->SetCommand(changeCommand);
    control->RunCommand( );
    delete changeCommand;

    // 换了好久频道也没找到好看的, 郁闷的关掉电视机
    // 创建一个关闭的命令, 这个命令只操纵tv这个对象
    Command *closeCommand = new CloseCommand(tv);
    control->SetCommand(closeCommand);
    control->RunCommand( );
    delete closeCommand;

    delete tv;
    delete control;
    return 0;
}

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据