C++命令模式:Command Pattern

把方法调用封装起来,调用此方法的对象不需要关心方法是怎么运行的,还可以重复使用这些封装来实现撤销(undo)。

案例:设计一个遥控器来控制不同类型的装置,比如热水器,空调,每个装置的具体步骤方法不一样。命令模式可以将“动作的请求者”从“动作的执行者”对象中解耦。在本案例中,请求者可以是遥控器,执行者就是各种装置其中之一。

如果遥控器上的每个按钮都存储一个命令对象,当按钮被按下的时候,就可以请命令对象做相关的工作。遥控器不需要知道工作内容是什么,只要相应的命令对象能和正确的对象沟通 ,把事情做好了就行。这样,遥控器就和具体的各个装置对象之间解耦了。

命令模式:把“请求”封装成对象,以便使用不同的请求。命令模式也支持可撤销的操作。

类图:

实现如下:

command.h

#ifndef COMMAND_H
#define COMMAND_H
#include <iostream>
#include <string>

using namespace std;

class Command {
public:
    virtual void execute() = 0;
    virtual void undo() = 0;
};

#endif // COMMAND_H


lightOnCommand.h  

#ifndef LIGHTONCOMMAND_H
#define LIGHTONCOMMAND_H
#include "command.h"
#include "light.h"

class LightOnCommand : public Command {
public:
    virtual void execute() override;
    virtual void undo() override;
private:
    Light* m_light = new Light();
};

void LightOnCommand::execute()
{
    m_light->on();
}

void LightOnCommand::undo()
{
    m_light->off();
}

#endif // LIGHTONCOMMAND_H

lightOffCommand.h

#ifndef LIGHTOFFCOMMAND_H
#define LIGHTOFFCOMMAND_H
#include "command.h"
#include "light.h"

class LightOffCommand : public Command {
public:
    virtual void execute() override;
    virtual void undo() override;
private:
    Light* m_light = new Light();
};

void LightOffCommand::execute()
{
    m_light->off();
}

void LightOffCommand::undo()
{
    m_light->on();
}
#endif // LIGHTOFFCOMMAND_H


noCommand.h

#ifndef NOCOMMAND_H
#define NOCOMMAND_H

// 空实现
class NoCommand : public Command {
public:
    virtual void execute() override;
    virtual void undo() override;
};

void NoCommand::execute()
{
    cout << "NoCommand" << endl;
}

void NoCommand::undo()
{
    cout << "NoCommand" << endl;
}
#endif // NOCOMMAND_H

remoteControl.h

#ifndef REMOTECONTROL_H
#define REMOTECONTROL_H
#include "command.h"
#include "noCommand.h"
#include <vector>


class RemoteControl {
public:
    void setCommand(int slot, Command *onCommand, Command *offCommand);
    void onButtonWasPressed(int slot);
    void offButtonWasPressed(int slot);
    void undoButtonWasPressed();

private:
    // 设置只有3个槽
    // vector<Command* > m_onCommands{{(Command*)NULL}, {(Command*)NULL}, {(Command*)NULL}};
    // vector<Command* > m_offCommands{{(Command*)NULL}, {(Command*)NULL}, {(Command*)NULL}};
    // 使用上面的写法也可以,不过更好的写法是使用NoCommand(null object);

    vector<Command* > m_onCommands{3, (Command *) new NoCommand()};
    vector<Command* > m_offCommands{3, (Command *) new NoCommand()};
    Command* m_undoCommand; // 保存上一次的操作
};

void RemoteControl::setCommand(int slot, Command *onCommand, Command *offCommand)
{
    m_onCommands[slot] = onCommand;
    m_offCommands[slot] = offCommand;
}

void RemoteControl::onButtonWasPressed(int slot)
{
    m_onCommands[slot]->execute();
    m_undoCommand = m_onCommands[slot];
}

void RemoteControl::offButtonWasPressed(int slot)
{
    m_offCommands[slot]->execute();
    m_undoCommand = m_offCommands[slot];
}

void RemoteControl::undoButtonWasPressed()
{
    m_undoCommand->undo();
}

#endif // REMOTECONTROL_H

light.h

#ifndef LIGHT_H
#define LIGHT_H
#include <iostream>
using namespace std;

class Light {
public:
    void on();
    void off();
};

void Light::on()
{
    cout << "灯打开了!" << endl;
}

void Light::off()
{
    cout << "灯关闭了!" << endl;
}

#endif // LIGHT_H

main.cpp

/*
 * 命令模式
 *
 * date:2023-9-11
*/

#include "remoteControl.h"
#include "lightOnCommand.h"
#include "lightOffCommand.h"

int main()
{
    // 注意:此遥控器只有3个槽,0,1,2
    RemoteControl* remoteControl = new RemoteControl();

    Command* lightOnCommand = new LightOnCommand();
    Command* lightOffCommand = new LightOffCommand();

    cout << "购买遥控器,使用1号槽来控制灯的开关" << endl;

    remoteControl->setCommand(1, lightOnCommand, lightOffCommand);

    cout << "开灯" << endl;

    remoteControl->onButtonWasPressed(1);

    cout << "关灯" << endl;

    remoteControl->offButtonWasPressed(1);

    cout << "撤销刚才的操作" << endl;

    remoteControl->undoButtonWasPressed();
}

运行截图:

注: 1.要想实现undo功能,就必须在遥控器类中添加一个对象来记录上一次的命令是什么。

        2.如果要一个槽同时控制多个装置,可以用数组分别存储要控制打开和关闭的所有装置对象。

NoCommand对象是一个空对象(null object)的例子。当你不想返回一个有意义的对象时,空对象就很有用。客户可以把处理null的责任转移给空对象。比如说,遥控器在出厂时肯定没有设置有意义的命令对象,就可以使用NoCommand对象作为代用品,当调用它的execute()方法时,这种对象什么事情都不做。

在许多设计模式中,都会看到空对象的使用。甚至有些时候,空对象本身也被视为是一种设计模式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值