设计模式(十九)——命令模式

一、命令模式简介

1、命令模式简介

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

    命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分割开。

设计模式(十九)——命令模式_第1张图片

    命令模式关键就是一个请求封装到一个类中(Command),再提供处理对象(Receiver),最后Command命令由Invoker激活。另外,可以将请求接收者的处理抽象出来作为参数传给Command对象,实际也就是回调的机制来实现这一点。处理操作方法地址通过参数传递给Command对象,Command对象在适当的时候再调用该函数。

    命令模式将调用操作的对象和知道如何实现操作的对象解耦,Invoker对象根本不知道具体的是哪个对象在处理Execute操作(当然要知道是Command类别的对象)。
Command要增加新的处理操作对象很容易,可以通过创建新的继承自Command的子类来实现这一点。
    命令模式可以和备忘录模式结合起来,支持取消的操作。

命令模式的本质是对命令进行封装,将发出命令的责任和执行命令的责任分割开。每一个命令都是一个操作:请求的一方发出请求,要求执行一个操作;接收的一方收到请求,并执行操作命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的。命令模式使请求本身成为一个对象,这个对象和其他对象一样可以被存储和传递。命令模式的关键在于引入了抽象命令接口,且发送者针对抽象命令接口编程,只有实现了抽象命令接口的具体命令才能与接收者相关联。

2、命令模式角色

    接收者(Receiver): 知道如何实施与执行一个请求相关的操作。任何类都可能作为一个接收者只要能够实现命令要求实现的相应功能。

    抽象命令类(Command): 用于声明执行操作的接口。

    具体命令类(ConcreteCommand): 创建一个具体命令对象并设定接收者。通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。 将一个接收者对象绑定于一个操作,调用接收者相应的操作,以实现Execute

    调用者(Invoker): 要求命令执行这个请求。通常会持有命令对象,可以持有多个命令对象。 

    客户类(Client): 创建具体的命令对象,并且设置命令对象的接收者。真正使用命令的客户端是从Invoker来触发执行。 

命令模式通过将请求封装到一个对象Command中,并将请求的接收者存放到具体的ConcreteCommand类中,从而实现调用操作的对象和操作的具体实现者之间的解耦。
命令模式结构图中,将请求的接收者(处理者)放到Command的具体子类ConcreteCommand中,当请求到来时(Invoker发出Invoke消息激活Command对象)ConcreteCommand将处理请求交给Receiver对象进行处理。

3、命令模式优缺点

    命令模式的优点:
    A可将多个命令装配成一个组合命令能较容易地设计一个命令队列
    B在需要的情况下,可以较容易地将命令记入日志
    C允许接收请求的一方决定是否要否决请求。
    D可以容易地实现对请求的撤销和重做
    E增加新的具体命令类很容易,因为加进新的具体命令类不影响其他的类

 F降低系统的耦合度:Command模式将调用操作的对象与知道如何实现该操作的对象解耦。

    命令模式的缺点:

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

4、命令模式使用场景

    命令模式使用场景:

    A系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。

    B系统需要在不同的时间指定请求、将请求排队和执行请求。

    C系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。

    D系统需要将一组操作组合在一起,即支持宏命令。

二、命令模式实现

Invoker调用者类:

#ifndef INVOKER_H
#define INVOKER_H
#include "Command.h"
 
class Invoker
{
public:
    //装载具体的命令对象
    void setCommand(Command *com)
    {
        m_pCom = com;
    }
    void Invoke()
    {
        //执行持有的命令
        m_pCom->Execute();
    }
private:
    Command* m_pCom;
};
 
#endif // INVOKER_H


Command抽象命令类:

#ifndef COMMAND_H
#define COMMAND_H
#include "Receiver.h"
 
//命令抽象类
class Command
{
public:
    //命令执行接口
    virtual void Execute() = 0;
protected:
    Command(){}
};
 
#endif // COMMAND_H


 

ConcreteCommandA具体命令类:

#ifndef CONCRETECOMMANDA_H
#define CONCRETECOMMANDA_H
#include "Command.h"
#include "Receiver.h"
 
//具体命令类
class ConcreteCommandA : public Command
{
public:
    //具体命令对象并设定其接收者
    ConcreteCommandA(Receiver* pReceiver)
    {
        m_pReceiver = pReceiver;
    }
    void Execute()
    {   //执行接收者绑定的操作
        m_pReceiver->Action();
    }
private:
    Receiver* m_pReceiver;
};
 
#endif // CONCRETECOMMANDA_H


ConcreteCommandB具体命令类:

#ifndef CONCRETECOMMANDB_H
#define CONCRETECOMMANDB_H
#include "Command.h"
#include "Receiver.h"
 
//具体命令类
class ConcreteCommandB : public Command
{
public:
    //具体命令对象并设定其接收者
    ConcreteCommandB(Receiver* pReceiver)
    {
        m_pReceiver = pReceiver;
    }
    void Execute()
    {   //执行接收者绑定的操作
        m_pReceiver->Action();
    }
private:
    Receiver* m_pReceiver;
};
 
#endif // CONCRETECOMMANDB_H


Receiver接收者类:

#ifndef RECEIVER_H
#define RECEIVER_H
#include 
using namespace std;
 
//接收者
class Receiver
{
public:
    //请求实际执行的操作
    void Action()
    {
         cout << "Receiver::Action()" << endl;
    }
};
 
#endif // RECEIVER_H


客户调用程序:

#include "Command.h"
#include "ConcreteCommandA.h"
#include "ConcreteCommandB.h"
#include "Receiver.h"
#include "Invoker.h"
 
int main()
{
    //创建命令的接收者
    Receiver* pReceiver = new Receiver();
    //指定命令的接收者创建命令
    Command* pCom = new ConcreteCommandA(pReceiver);
    //创建请求者
    Invoker* pInvoker = new Invoker();
    //设置调用的命令
    pInvoker->setCommand(pCom);
    pInvoker->Invoke();//执行命令
 
    delete pReceiver;
    delete pCom;
    delete pInvoker;
    return 0;
}


三、命令模式实例

    餐厅业务流程中,通过服务员来点菜,顾客产生订单请求,订单中具体是哪位厨师做菜顾客并不知道顾客菜单请求者,厨师是菜单实现者,产生的菜单订单即命令对象,服务员可能会接到多个订单,并将订单分配给厨师,厨师根据接到的菜单做菜。餐厅只提供川菜和粤菜两种菜系选择。

设计模式(十九)——命令模式_第2张图片

Command抽象类:

#ifndef COMMAND_H
#define COMMAND_H
#include 
#include 
#include "Master.h"
using namespace std;
 
//Command抽象类,订单
class Command
{
public:
    //命令对象通知Receiver的接口
    virtual void cooking() = 0;
protected:
    Command(Master* master)
    {
        m_pMaster = master;
    }
protected:
    Master* m_pMaster;
};
 
#endif // COMMAND_H


CantoneseCuisine粤菜订单类:

#ifndef CANTONESECUISINE_H
#define CANTONESECUISINE_H
#include "Command.h"
 
//具体Command,粤菜订单
class CantoneseCuisine : public Command
{
public:
    CantoneseCuisine(Master* master):Command(master)
    {}
    //通知厨师接到新的粤菜订单
    void cooking()
    {
        cout << "CantoneseCuisineMaster: an new CantoneseCuisine order" << endl;
        //厨师做菜
        m_pMaster->doAction();
    }
};
 
#endif // CANTONESECUISINE_H


SiChuanFood川菜订单类:

#ifndef SICHUANFOOD_H
#define SICHUANFOOD_H
#include "Command.h"
 
//具体Command,川菜订单
class SiChuanFood : public Command
{
public:
    SiChuanFood(Master* master):Command(master)
    {}
    //通知厨师接到新的川菜订单
    void cooking()
    {
        cout << "SiChuanFoodMaster: an new SiChuanFood order" << endl;
        //厨师做菜
        m_pMaster->doAction();
    }
};
 
#endif // SICHUANFOOD_H


Master厨师抽象类:

#ifndef MASTER_H
#define MASTER_H
#include 
using namespace std;
 
//Receiver抽象类,厨师
class Master
{
public:
    virtual void doAction() = 0;
protected:
    Master(){}
};
 
#endif // MASTER_H


 

CantoneseCuisineMaster粤菜厨师类:

#ifndef CANTONESECUISINEMASTER_H
#define CANTONESECUISINEMASTER_H
#include "Master.h"
 
//Receiver具体实现类,粤菜厨师
class CantoneseCuisineMaster : public Master
{
public:
    //粤菜厨师做菜
    void doAction()
    {
        cout << "CantoneseCuisineMaster is cooking" << endl;
    }
};
 
#endif // CANTONESECUISINEMASTER_H


SiChuanFoodMaster川菜厨师类:

#ifndef SICHUANFOODMASTER_H
#define SICHUANFOODMASTER_H
#include "Master.h"
 
//Receiver具体实现类,川菜厨师
class SiChuanFoodMaster : public Master
{
public:
    //川菜厨师做菜
    void doAction()
    {
        cout << "SiChuanFoodMaster is cooking" << endl;
    }
};
 
#endif // SICHUANFOODMASTER_H


Waiter服务员类:

#include 
#include 
#include "Command.h"
#include "SiChuanFood.h"
#include "CantoneseCuisine.h"
 
using namespace std;
 
//Invoker类,服务员
class Waiter
{
public:
    Waiter()
    {
        m_commands = new vector;
    }
    ~Waiter()
    {
        delete m_commands;
    }
    //装载订单到订单池
    void setOrder(Command* command)
    {
        time_t now=time(0);
        //判断订单的类型并分别做不同的处理
        if(typeid(*command) == typeid(SiChuanFood))
        {
            //川菜
            cout<<"plog: Waiter receive an new order. Cuisine: " << 
typeid(*command).name()<<" Time:" << asctime(gmtime(&now));
            m_commands->push_back(command);
        }
        else if(typeid(*command) == typeid(CantoneseCuisine))
        {
            //粤菜
            m_commands->push_back(command);
            cout<<"plog: Waiter receive an new order. Cuisine: " 
<< typeid(*command).name()<<" Time:" << asctime(gmtime(&now));
        }
        else
        {   //没有提供其他菜系
            cout << "plog: Waiter: No service at now" << endl;
        }
    }
    //通知厨师做菜
    void notify()
    {
        vector::iterator iter;
        for(iter = m_commands->begin(); iter != m_commands->end(); ++iter)
            (*iter)->cooking();
    }
protected:
    vector* m_commands;//订单对象池
};
 
#endif // WAITER_H


客户调用程序:

#include "Master.h"
#include "CantoneseCuisineMaster.h"
#include "SiChuanFoodMaster.h"
#include "Command.h"
#include "CantoneseCuisine.h"
#include "SiChuanFood.h"
#include "Waiter.h"
 
int main()
{
    //生成川菜厨师
    Master* siChuanFoodMaster = new SiChuanFoodMaster();
    //生成粤菜厨师
    Master* cantoneseCuisineMaster = new CantoneseCuisineMaster();
    //顾客产生川菜订单
    Command* siChuanFood = new SiChuanFood(siChuanFoodMaster);
    //顾客产生粤菜订单
    Command* cantoneseCuisine = new CantoneseCuisine(cantoneseCuisineMaster);
    //生成服务员
    Waiter* waiter = new Waiter();
    
    //将顾客订单对象推送到订单队列
    waiter->setOrder(siChuanFood);
    waiter->setOrder(cantoneseCuisine);
    
    //服务员通知厨师已经接到的需求订单
    waiter->notify();
    
    delete siChuanFoodMaster,cantoneseCuisineMaster;
    delete siChuanFood,cantoneseCuisine,waiter;
    return 0;
}