设计模式(16)命令模式

命令模式简介

老样子,为了便于理解这个模式,先举个生活中类似的例子。比如,在使用手机买东西的时候,点了一下付款,然后输入密码就可以完成购物,在点击付款的时候,用户是不会在意手机后台调用了哪些函数,与后台发送了什么数据,进行了哪些安全的校验,同样,手机与后台也同样不知道,具体是谁在使用这个手机和软件,命令的发送方和接收方没有任何实际关联。

在设计模式中,命令模式就是解耦了命令的发送者和接受者,发送者和接受者之间没有直接引用关系,发送者只要知道如何发送请求,不必知道请求是如何完成的。

命令模式:将一个请求封装为一个对象,从而可用不同的请求对客户进行参数化,对请求排队或者记录请求日志,以及支持可撤销的操作。

命令模式结构

在这里插入图片描述
命令模式的结构如上图,命令模式一共有以下几种角色:

  • Command(抽象命令类):是一个抽象类,声明了用于执行命令的接口execute()。
  • ConcreteCommand(具体命令类):具体的命令类,实现了执行命令的接口execute(),它对应具体的接收者对象,将接收者(Receiver)的动作action()绑定其中。在execu()方法中将调用接收者的动作action()。(这就是定义中的“将请求封装成一个对象”的体现)
  • Invoker(调用者):请求的发送者,通过命令对象来执行请求。一个调用者不需要在设计时确定其接收者,所以调用者通过聚合,与命令类产生关联。具体实现中,可以将一个具体命令对象注入到调用者中,再通过调用具体命令对象的execute()方法,实现简介请求命令执行者(接收者)的操作。
  • Receiver(接收者): 实现处理请求的具体操作(action)。

命令模式代码实例

CommandPattern.h

#include <mutex>
#include <time.h>

using namespace std;

//  命令队列类
#define COMMAND_QUEUE

// 抽象命令类 Command
class Command
{
    
    
public:
  Command() {
    
    }
  // 声明抽象接口:发送命令
  virtual void execute() = 0;

private:
  Command *command;
};

// 接收者:电灯类
class Lamp
{
    
    
public:
  Lamp()
  {
    
    
    this->lampState = false;
  }

  void on()
  {
    
    
    lampState = true;
    cout << "Lamp is on\n";
  }

  void off()
  {
    
    
    lampState = false;
    cout << "Lamp is off\n";
  }

  bool getLampState()
  {
    
    
    return lampState;
  }

private:
  bool lampState;
};

// 接收者:风扇类
class Fan
{
    
    
public:
  Fan()
  {
    
    
    this->fanState = false;
  }

  void on()
  {
    
    
    fanState = true;
    cout << "Fan is on\n";
  }

  void off()
  {
    
    
    fanState = false;
    cout << "Fan is off\n";
  }

  bool getFanState()
  {
    
    
    return fanState;
  }

private:
  bool fanState;
};

// 具体命令类 LampCommand
class LampCommand : public Command
{
    
    
public:
  LampCommand()
  {
    
    
    cout << "开关控制电灯\n";
    lamp = new Lamp();
  }

  // 实现execute()
  void execute()
  {
    
    
    if (lamp->getLampState())
    {
    
    
      lamp->off();
    }
    else
    {
    
    
      lamp->on();
    }
  }

private:
  Lamp *lamp;
};

// 具体命令类 FanCommand
class FanCommand : public Command
{
    
    
public:
  FanCommand()
  {
    
    
    cout << "开关控制风扇\n";
    fan = new Fan();
  }

  // 实现execute()
  void execute()
  {
    
    
    if (fan->getFanState())
    {
    
    
      fan->off();
    }
    else
    {
    
    
      fan->on();
    }
  }

private:
  Fan *fan;
};

// 调用者 Button
class Button
{
    
    
public:
  Button() {
    
    }

  // 注入具体命令类对象
  void setCommand(Command *cmd)
  {
    
    
    this->command = cmd;
  }

  // 发送命令:触摸按钮
  void touch()
  {
    
    
    cout << "触摸开关:";
    command->execute();
  }

private:
  Command *command;
};

#ifdef COMMAND_QUEUE
/*************************************/
/*             命令队列              */
#include <vector>

// 命令队列类
class CommandQueue
{
    
    
public:
  CommandQueue() {
    
    }

  void addCommand(Command *cmd)
  {
    
    
    commandQueue.push_back(cmd);
  }

  void execute()
  {
    
    
    for (int i = 0; i < commandQueue.size(); i++)
    {
    
    
      commandQueue[i]->execute();
    }
  }

private:
  vector<Command *> commandQueue;
};

// 调用者
class Button2
{
    
    
public:
  Button2() {
    
    }

  // 注入具体命令队列类对象
  void setCommandQueue(CommandQueue *cmdQueue)
  {
    
    
    this->cmdQueue = cmdQueue;
  }
  
  // 发送命令:触摸按钮
  void touch()
  {
    
    
    cout << "触摸开关:";
    cmdQueue->execute();
  }

private:
  CommandQueue *cmdQueue;
};

#endif

CommandPattern.cpp

#include <iostream>
#include "CommandPattern.h"

int main()
{
    
    
	// 实例化调用者:按钮
	Button *button = new Button();
	Command *lampCmd, *fanCmd;

	// 按钮控制电灯
	lampCmd = new LampCommand();
	button->setCommand(lampCmd);
	button->touch();
	button->touch();
	button->touch();
	cout << "\n";

	// 按钮控制风扇
	fanCmd = new FanCommand();
	button->setCommand(fanCmd);
	button->touch();
	button->touch();
	button->touch();

#ifdef COMMAND_QUEUE

	cout << "\n***********************************\n";

	Button2 *button2 = new Button2();
	Command *lampCmd2, *fanCmd2;
	CommandQueue *cmdQueue = new CommandQueue();

	// 按钮控制电灯
	lampCmd2 = new LampCommand();
	cmdQueue->addCommand(lampCmd2);

	// 按钮控制风扇
	fanCmd2 = new FanCommand();
	cmdQueue->addCommand(fanCmd2);

	button2->setCommandQueue(cmdQueue);
	button2->touch();

#endif

	cout << "\n";

	delete button;
	delete lampCmd;
	delete fanCmd;
	delete button2;
	delete lampCmd2;
	delete fanCmd2;

	return 0;
}

命令模式总结

优点:

  • 降低系统耦合度,将命令的请求者与接收者分离解耦,请求者和发送者不存在直接关联,各自独立互不影响。
  • 便于扩展:新的命令很容易加入到系统中,且符合开闭原则。
  • 较容易实现命令队列或宏命令。
  • 为请求的撤销和回复操作提供了一种设计实现方案。

缺点:

  • 命令模式可能导致系统中有过多的具体命令类,增加了系统中对象的数量。

适用环境:

  • 系统需要将请求发送者和接收者解耦,使得发送者和接收者互不影响。
  • 系统需要在不同时间指定请求、将请求排队和执行请求。
  • 系统需要支持命令的撤销和恢复操作。
  • 系统需要将一组操作组合在一起形成宏命令。

Guess you like

Origin blog.csdn.net/qq_24649627/article/details/115399585