命令模式 詳解

GeorgeWang1994發表於2016-07-02

定義

旨在將方法呼叫、請求或操作封裝到單一物件中,從而根據我們不同的請求對客戶進行引數化和傳遞可供執行的方法呼叫。此外,這種模式將呼叫操作的物件與知道如何實現該操作的物件解耦,並在交換出具體類(物件)方面提供更大的整體靈活性;

它為我們提供了一種分離職責的手段,這些職責包括從執行命令的任意地方釋出命令以及將該職責轉而委託給不同物件;

行為型模式

角色

  • 命令角色(Command):是一個抽象類,類中對需要執行的命令進行宣告,一般來說是對外公佈一個excute用來實行命令;
  • 具體命令角色(ConcreteCommand):實現類,對抽象類中宣告的方法進行實現;
  • 呼叫者(Invoker): 呼叫者,負責呼叫命令;
  • 接受者(Recevie): 接受者,負責接收命令並且執行命令;
  • 客戶端(Client): 最終的客戶端呼叫類;

從網上找到的例圖
enter image description here


適用場景

  • 使用命令模式作為“CallBack”在物件導向系統中的替代。“CallBack”講的便是先將一個函式登記上,然後在以後呼叫此函式;
  • 需要在不同的時間指定請求、將請求排隊。一個命令物件和原先的請求發出者可以有不同的生命期。換言之,原先的請求發出者可能已經不在了,而命令本身仍然是活動的。這時命令的接收者可以是在本地,也可以是在網路的另外一個地址。命令物件可以在串形化之後傳送到另外一臺機器上;
  • 系統需要支援命令的撤銷操作,命令物件可以把狀態儲存起來,等到客戶端需要撤銷命令所產生的效果時,可以呼叫撤銷方法,把命令所產生的效果撤銷掉;還有恢復操作,以供客戶端在需要的時候,再重新實施命令效果;
  • 系統需要將一組操作組合在一起,即支援巨集命令;
  • 如果一個系統要將系統中所有的資料更新到日誌裡,以便在系統崩潰時,可以根據日誌裡讀回所有的資料更新命令,重新呼叫Execute方法一條一條執行這些命令,從而恢復系統在崩潰前所做的資料更新;

例子

比如操控電視,這就是個典型的命令模式;電視作為接受者,被遙控器這個呼叫者操控,遙控器又有多個命令,它們有統一的介面;都會被遙控器呼叫;


實現程式碼

/**
 * Created by George on 16/7/2.
 */
//命令介面
var Command = function () {
    this.execute = function () {

    };
};

//命令接收者Receiver
var TV = function () {
    this.curChannel = 0;
    this.turnOn = function () {
        console.log("the tv is on");
    };
    this.turnOff = function () {
        console.log("the tv is off");
    };
    this.changeChannel = function (channel) {
        this.curChannel = channel;
        console.log("now the tv is " + this.curChannel);
    };
};

//開機命令
var CommandOn = function (tv) {
    Command.apply(this);
    this.tv = tv;
    this.execute = function () {
        this.tv.turnOn();
    };
};


//關機命令
var CommandOff = function (tv) {
    Command.apply(this);
    this.tv = tv;
    this.execute = function () {
        this.tv.turnOff();
    };
};

//切換頻道命令
var CommandChange = function (tv, channel) {
    Command.apply(this);
    this.tv = tv;
    this.channel = channel;
    this.execute = function () {
        this.tv.changeChannel(this.channel);
    };
};

//遙控器作為Invoker
var Control = function (onCommand, offCommand, changeCommand) {
    this.onCommand = onCommand;
    this.offCommand = offCommand;
    this.changeCommand = changeCommand;

    this.turnOn = function () {
        this.onCommand.execute();
    };

    this.turnOff = function () {
        this.offCommand.execute();
    };

    this.changeChannel = function () {
        this.changeCommand.execute();
    };
};

//電視
var tv = new TV();
//開電視命令
var commandOn = new CommandOn(tv);
//關電視命令
var commandOff = new CommandOff(tv);
//切換命令
var commandChange = new CommandChange(tv, 2);
//遙控器
var control = new Control(commandOn, commandOff, commandChange);
control.turnOn();
control.turnOff();
control.changeChannel();
  1. 命令模式的本質是對命令進行封裝,將發出命令的責任和執行命令的責任分隔開;
  2. 每一個命令都是一個操作:請求的一方發出請求,要求執行一個操作;接收的一方收到請求,並執行操作;
  3. 命令模式允許請求的一方和接收的一方獨立分開,使得請求的一方不必知道接收請求的一方的介面,更不必知道請求是如何被接收,以及操作是否被執行,何時被執行以及執行內容;
  4. 命令模式使請求本身成為一個物件,這個物件和其他物件一樣可以被儲存和傳遞;
  5. 命令模式的關鍵在於引入了抽象命令介面,而且傳送者針對抽象命令介面程式設計,只有實現了抽象命令介面的具體命令才能接收者相關聯;

優缺點

  1. 將呼叫操作的物件和知道如何實現該操作的物件解耦;
  2. Command是頭等物件,可以像其它物件一樣被操作和擴充套件;
  3. 可將多個命令裝配成一個符合命令;
  4. 呼叫同一方法實現不同的功能;

注意的是
1. 如果存在過多的具體命令,這將會影響命令模式的使用;