命令模式

概念

命令模式将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。命令模式又称为动作(Action)模式或事务(Transaction)模式。

适用场景

在软件系统中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将“行为请求者”与“行为实现者”解耦?
常见用途
- 队列请求
命令可以将运算块打包(一个接受者和一组动作),然后将它传来传去,就像是一般的对象一样。现在,即使在命令对象被创建许久之后,运算依然可以被调用。事实上,它甚至可以在不同的线程中被调用。我们可以利用这样的特性衍生一些应用,例如:日程安排(Scheduler)、线程池、工作队列等。
- 日志请求
某些应用需要我们将所有的动作都记录在日志中,并能在系统死机之后,重新调用这些动作恢复到之前的状态。通过新增两个方法(store()、load()),命令模式就能够支持这一点。在许多调用大型数据结构的动作的应用无法再每次改变发生时被快速的存储。通过使用记录日志,我们可以将上次检查点(checkpoint)之后的所有操作记录下来,如果系统出现状况,从检查点开始应用这些操作。对更高级的应用而言,这些技巧可以被扩展应用到事务(transaction)处理中,也就是说,一整群操作必须全部进行完成,或者没有进行任何的操作。

结构

UML图

  • 抽象命令类(Command):声明执行操作的接口。调用接收者相应的操作,以实现执行的方法execute(),和其他的undo(),redo(),store()等方法
  • 具体命令类(ConcreteCommand):通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。
  • 调用者(Invoker) :将请求付诸实践。通常会持有命令对象,可以持有很多的命令对象。
  • 接受者(Receiver):知道如何实施与执行一个请求相关的操作。任何类都可能作为一个接收者,只要它能够实现命令要求实现的相应功能。
  • 客户类(Client):创建具体的命令对象,并且设置命令对象的接收者。真正使用命令的客户端是从Invoker来触发执行。

优缺点

优点

  1. 降低系统的耦合度–命令模式将发出请求的对象和执行请求的对象解耦
  2. 动作的执行被封装成类,该类的对象可以像其他对象一样被操纵和扩展
  3. 组合命令:可将多个命令装配成一个组合命令,即可以比较容易地设计一个命令队列和宏命令(组合命令)
  4. 调用者可以接收命令当做参数,甚至在运行时动态的进行
  5. 可以方便的实现undo和redo

缺点

使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个命令都需要设计一个具体命令类,因此某些系统可能需要大量具体命令类,这将影响命令模式的使用。

#include <iostream>
#include <memory>
#include <vector>
#include <stdexcept>
#include <string>
#include <sstream>

using namespace std;

// 电灯接收者类
class Light
{
public:
    void on()
    {
        cout << "light is on" << endl;
    }

    void off()
    {
        cout << "light is off" << endl;
    }
};

// 音响接收者类
class Stereo
{
public:
    void on()
    {
        cout << "stereo is on" << endl;
    }

    void off()
    {
        cout << "stereo is off" << endl;
    }

    void setCD()
    {
        cout << "stereo is set for CD input" << endl;
    }

    void setVolume(int volume)
    {
        cout << "stereo volume set to " << volume << endl;
    }
};

// 抽象命令类
class Command
{
public:
    virtual void execute() = 0;
    virtual string className() = 0;
};

// 打开电灯命令类
class LightOnCommand : public Command
{
public:
    LightOnCommand(shared_ptr<Light> light)
        : m_light(light)
    {
        if (!light)
        {
            throw invalid_argument("[LightOnCommand::LightOnCommand] light is null.");
        }
    }

    void execute() override
    {
        m_light->on();
    }

    string className() override
    {
        return "LightOnCommand";
    }

private:
    shared_ptr<Light> m_light;
};

// 关闭电灯命令类
class LightOffCommand : public Command
{
public:
    LightOffCommand(shared_ptr<Light> light)
        : m_light(light)
    {
        if (!light)
        {
            throw invalid_argument("[LightOffCommand::LightOffCommand] light is null.");
        }
    }

    void execute() override
    {
        m_light->off();
    }

    string className() override
    {
        return "LightOffCommand";
    }

private:
    shared_ptr<Light> m_light;
};

// 打开音响命令类
class StereoOnWithCDCommand : public Command
{
public:
    explicit StereoOnWithCDCommand(shared_ptr<Stereo> stereo)
        : m_stereo(stereo)
    {
        if(!stereo)
        {
            throw invalid_argument("[StereoOnWithCDCommand::StereoOnWithCDCommand] stereo is null.");
        }
    }

    void execute() override
    {
        m_stereo->on();
        m_stereo->setCD();
        m_stereo->setVolume(5);
    }

    string className() override
    {
        return "StereoOnWithCDCommand";
    }

private:
    shared_ptr<Stereo> m_stereo;
};

// 关闭音响命令类
class StereoOffCommand : public Command
{
public:
    explicit StereoOffCommand(shared_ptr<Stereo> stereo)
        : m_stereo(stereo)
    {
        if (!stereo)
        {
            throw invalid_argument("[StereoOffCommand::StereoOffCommand] stereo is null.");
        }
    }

    void execute() override
    {
        m_stereo->off();
    }

    string className() override
    {
        return "StereoOffCommand";
    }

private:
    shared_ptr<Stereo> m_stereo;
};

// 空命令类
class EmptyCommand : public Command
{
public:
    void execute() override
    {
        cout << "[EmptyCommand::execute]this is empty command, do nothing." << endl;
    }

    string className() override
    {
        return "EmptyCommand";
    }
};

// 调用者类
class RemoteControl
{
public:
    enum Slot
    {
        Slot1 = 0,
        Slot2,

        NSlot
    };
    RemoteControl()
    {
        auto emptyCmd = make_shared<EmptyCommand>();
        m_onCommands.assign(NSlot, emptyCmd);
        m_offCommands.assign(NSlot, emptyCmd);
    }

    void print() const
    {
        cout << "--------------Rmote Control--------------" << endl;
        for (decltype(m_onCommands.size()) i = 0; i < m_onCommands.size(); ++i)
        {
            cout << "[slot " << i << "]" << '\t' << m_onCommands[i]->className() << '\t' << m_offCommands[i]->className() << "\n";
        }

        cout << endl;
    }

    void setCommand(int slot, shared_ptr<Command> onCommand, shared_ptr<Command> offCommand)
    {
        check(slot);

        m_onCommands[slot] = onCommand;
        m_offCommands[slot] = offCommand;
    }

    void onButtonWasPushed(int slot)
    {
        check(slot);
        cout << "slot " << slot << " on button was pushed" << endl;
        m_onCommands[slot]->execute();
    }

    void offButtonWasPushed(int slot)
    {
        check(slot);
        cout << "slot " << slot << " off button was pushed" << endl;
        m_offCommands[slot]->execute();
    }

private:
    void check(int slot)
    {
        if (slot < 0 || slot >= NSlot)
        {
            ostringstream oss;
            oss << "slot " << slot << " is not exist.";
            throw invalid_argument(oss.str());
        }
    }

private:
    vector<shared_ptr<Command>> m_onCommands;
    vector<shared_ptr<Command>> m_offCommands;
};

int main()
{
    // 客户代码
    auto light = make_shared<Light>();
    auto stereo = make_shared<Stereo>();

    auto lightOnCmd = make_shared<LightOnCommand>(light);
    auto lightOffCmd = make_shared<LightOffCommand>(light);

    auto stereoOnCmd = make_shared<StereoOnWithCDCommand>(stereo);
    auto stereoOffCmd = make_shared<StereoOffCommand>(stereo);

    RemoteControl remoteControl;
    remoteControl.setCommand(RemoteControl::Slot1, lightOnCmd, lightOffCmd);
    remoteControl.setCommand(RemoteControl::Slot2, stereoOnCmd, stereoOffCmd);

    remoteControl.print();

    remoteControl.onButtonWasPushed(RemoteControl::Slot1);
    remoteControl.offButtonWasPushed(RemoteControl::Slot1);
    remoteControl.onButtonWasPushed(RemoteControl::Slot2);
    remoteControl.offButtonWasPushed(RemoteControl::Slot2);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值