1. 程式人生 > >設計模式-第十五篇命令模式

設計模式-第十五篇命令模式

1. 定義

       命令模式是一個高內聚的模式,其定義為:Encapsulate  a request as  an object , thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.(將一個請求封裝成一個物件,從而讓你使用不同的請求把客戶端引數化,對請求排隊或者記錄請求日誌,可以提供命令的撤銷和恢復功能。)

2. 通用類圖


在該類圖中,我們看到三個角色:

  • Receiver接受者角色

   該角色就是幹活的角色,命令傳遞到這裡是應該被執行的,具體到我們上面的例子中就是Group的三個實現類。

  • Command命令角色

   需要執行的所有命令都在這裡宣告

  • Invoker呼叫者角色

   接收到命令,並執行命令。

3. 程式碼實現 

     命令模式比較簡單,但是在專案中非常頻繁地使用,因為它的封裝性非常好,把請求方(Invoker)和執行方(Receiver)分開了,擴充套件性也有很好的保障,通用程式碼比較簡單。

 通用Receiver類

package CommandPattern;

public abstract class Receiver {
    //抽象接受者,定義每個接受者都必須完成的業務
    public abstract void doSomething();
}

    很奇怪,為什麼Receiver是一個抽象類?那是因為接受者可以有多個,有多個就需要定義一個所有特性的抽象集合-抽象的接受者,其具體的接受者如下所示:

package CommandPattern;

public class ConcreteReceiver1 extends Receiver{
    //每個接受者都必須處理一定的業務邏輯
    public void doSomething()
    {

    }
}
package CommandPattern;

public class ConcreteReceiver2 extends Receiver{
    //每個接受者都必須處理一定的業務邏輯
    public void doSomething()
    {

    }
}

      接受者可以有N個,這要依賴業務的具體含義。命令角色是命令模式的核心,其抽象的命令類如下一樣:

package CommandPattern;

public abstract class Command {
    //每個命令類都必須有一個執行命令的方法
    public abstract void execute();
}

     根據環境的需求,具體的命令類也可以有N個,其實現類如下所示:

package CommandPattern;

public class ConcreteCommand1 extends Command{
    //對哪個Receiver類進行命令處理
    private Receiver receiver;
    //建構函式傳遞接受者
    public ConcreteCommand1(Receiver _receiver)
    {
        this.receiver=_receiver;
    }
    //必須實現一個命令
    public void execute()
    {
        this.receiver.doSomething();
    }
}
package CommandPattern;

public class ConcreteCommand2 extends Command{
    //對哪個Receiver類進行命令處理
    private Receiver receiver;
    //建構函式傳遞接受者
    public ConcreteCommand2(Receiver _receiver)
    {
        this.receiver=_receiver;
    }
    //必須實現一個命令
    public void execute()
    {
        this.receiver.doSomething();
    }
}

     定義了兩個具體的命令類,讀者可以在實際應用中擴充套件該命令類。在每個命令類中,通過建構函式定義了該命令是針對哪一個接受者發出的,定義一個命令接收的主體。呼叫者非常簡單,僅實現命令的傳遞。

     呼叫者Invoker類

package CommandPattern;

public class Invoker {
    private Command command;
    //接收包,接收命令
    public void setCommand(Command _command) {
        this.command = _command;
    }
    //接收命令
    public void action()
    {
        this.command.execute();
    }

}

  呼叫者就像受氣包,不管什麼命令,都要接收、執行。下面的程式碼時呼叫命令模式。

package CommandPattern;

public class Client {
    public static void main(String[] agrs)
    {
        //首先宣告呼叫者Invoker
        Invoker invoker=new Invoker();
        //定義接收者
        Receiver receiver=new ConcreteReceiver1();
        //定義一個發給接收者的命令
        Command command=new ConcreteCommand1(receiver);
        //把命令交給呼叫者去執行
        invoker.setCommand(command);
        invoker.action();
    }
}

3. 命令模式的應用

3.1 命令模式的優點:

  •    類間解耦

    呼叫者角色與接受者角色之間沒有任何依賴關係,呼叫者實現功能時只需要呼叫Command抽象類的execute方法就可以,不需要了解到底是哪個接收者執行的;

  •    可擴充套件性

    Command的子類非常容易擴充套件,而呼叫者Invoker和高層次的模組Client不產生嚴重的程式碼耦合

  •    命令模式結合其他模式會更優秀

    命令模式可以結合責任鏈模式,實現命令族解析任務;結合模板方法模式,則可以減少Command子類的膨脹問題。

3.2 命令模式的缺點

      命令模式也是有缺點的,請看Command子類:如果有N個命令,問題就出來了,Command的子類可不是幾個,而是N個,這個類膨脹得非常大,這個就需要慎重考慮了。

3.3 命令模式的使用場景

      只要你認為是命令的地方就可以使用命令模式。例如,在GUI的開發中,一個按鈕點選是一個命令,可以採用命令模式;模擬DOS命令的時候,當然也要採用命令模式;觸發-反饋機制的處理等。

4. 命令模式的擴充套件

    反悔問題,有兩種方法可以解決:一是結合備忘錄模式還原最後狀態,該方法適合接受者為狀態的變更情況,而不適合事件處理;而是通過增加一個新的命令,實現事件的回滾。