設計模式(十九)——命令模式

設計模式(十九)——命令模式

1、命令模式簡介

1、命令模式簡介

    命令模式將一個請求封裝爲一個對象,從而可用不一樣的請求對客戶進行參數化;對請求排隊或記錄請求日誌,以及支持可撤銷的操做。ios

    命令模式把請求一個操做的對象與知道怎麼執行一個操做的對象分割開。編程

wKioL1nVq9iih1c8AAC9BHjsgZA468.jpg

    命令模式關鍵就是一個請求封裝到一個類中(Command),再提供處理對象(Receiver),最後Command命令由Invoker激活。另外,能夠將請求接收者的處理抽象出來做爲參數傳給Command對象,實際也就是回調的機制來實現這一點。處理操做方法地址經過參數傳遞給Command對象,Command對象在適當的時候再調用該函數。設計模式

    命令模式將調用操做的對象和知道如何實現操做的對象解耦,Invoker對象根本不知道具體的是哪一個對象在處理Execute操做(固然要知道是Command類別的對象)。
Command要增長新的處理操做對象很容易,能夠經過建立新的繼承自Command的子類來實現這一點。
    命令模式能夠和備忘錄模式結合起來,支持取消的操做。ide

命令模式的本質是對命令進行封裝,將發出命令的責任和執行命令的責任分割開。每個命令都是一個操做:請求的一方發出請求,要求執行一個操做;接收的一方收到請求,並執行操做命令模式容許請求的一方和接收的一方獨立開來,使得請求的一方沒必要知道接收請求的一方的接口,更沒必要知道請求是怎麼被接收,以及操做是否被執行、什麼時候被執行,以及是怎麼被執行的。命令模式使請求自己成爲一個對象,這個對象和其餘對象同樣能夠被存儲和傳遞。命令模式的關鍵在於引入了抽象命令接口,且發送者針對抽象命令接口編程,只有實現了抽象命令接口的具體命令才能與接收者相關聯。函數

二、命令模式角色

    接收者(Receiver): 知道如何實施與執行一個請求相關的操做。任何類均可能做爲一個接收者只要可以實現命令要求實現的相應功能。ui

    抽象命令類(Command): 用於聲明執行操做的接口。spa

    具體命令類(ConcreteCommand): 建立一個具體命令對象並設定接收者。一般會持有接收者,並調用接收者的功能來完成命令要執行的操做。 將一個接收者對象綁定於一個操做,調用接收者相應的操做,以實現Execute設計

    調用者(Invoker): 要求命令執行這個請求。一般會持有命令對象,能夠持有多個命令對象。 日誌

    客戶類(Client): 建立具體的命令對象,而且設置命令對象的接收者。真正使用命令的客戶端是從Invoker來觸發執行。 對象

命令模式經過將請求封裝到一個對象Command中,並將請求的接收者存放到具體的ConcreteCommand類中,從而實現調用操做的對象和操做的具體實現者之間的解耦。
命令模式結構圖中,將請求的接收者(處理者)放到Command的具體子類ConcreteCommand中,當請求到來時(Invoker發出Invoke消息激活Command對象)ConcreteCommand將處理請求交給Receiver對象進行處理。

3、命令模式優缺點

    命令模式的優勢:
    A可將多個命令裝配成一個組合命令能較容易地設計一個命令隊列
    B在須要的狀況下,能夠較容易地將命令記入日誌
    C容許接收請求的一方決定是否要否決請求。
    D能夠容易地實現對請求的撤銷和重作
    E增長新的具體命令類很容易,由於加進新的具體命令類不影響其餘的類

 F下降系統的耦合度:Command模式將調用操做的對象與知道如何實現該操做的對象解耦。

    命令模式的缺點:

    使用命令模式可能會致使某些系統有過多的具體命令類。由於針對每個命令都須要設計一個具體命令類,所以某些系統可能須要大量具體命令類,影響命令模式的使用。

四、命令模式使用場景

    命令模式使用場景:

    A系統須要將請求調用者和請求接收者解耦,使得調用者和接收者不直接交互。

    B系統須要在不一樣的時間指定請求、將請求排隊和執行請求。

    C系統須要支持命令的撤銷(Undo)操做和恢復(Redo)操做。

    D系統須要將一組操做組合在一塊兒,即支持宏命令。

2、命令模式實現

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 <iostream>
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;
}


3、命令模式實例

    餐廳業務流程中,經過服務員來點菜,顧客產生訂單請求,訂單中具體是哪位廚師作菜顧客並不知道顧客菜單請求者,廚師是菜單實現者,產生的菜單訂單即命令對象,服務員可能會接到多個訂單,並將訂單分配給廚師,廚師根據接到的菜單作菜。餐廳只提供川菜和粵菜兩種菜系選擇。

wKioL1nVrAnjEEBlAADP593PNEM405.jpg

Command抽象類:

#ifndef COMMAND_H
#define COMMAND_H
#include <iostream>
#include <string>
#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 <iostream>
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 <ctime>
#include <typeinfo>
#include "Command.h"
#include "SiChuanFood.h"
#include "CantoneseCuisine.h"
 
using namespace std;
 
//Invoker類,服務員
class Waiter
{
public:
    Waiter()
    {
        m_commands = new vector<Command*>;
    }
    ~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<Command*>::iterator iter;
        for(iter = m_commands->begin(); iter != m_commands->end(); ++iter)
            (*iter)->cooking();
    }
protected:
    vector<Command*>* 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;
}
相關文章
相關標籤/搜索