設計模式之命令模式詳解(Command Pattern)

在軟件開發系統中,常常出現“方法的請求者”與“方法的實現者”之間存在緊密的耦合關係。這不利於軟件功能的擴展與維護。例如,想對行爲進行“撤銷、重做、記錄”等處理都很不方便,因此“如何將方法的請求者與方法的實現者解耦?”變得很重要,命令模式能很好地解決這個問題。

在現實生活中,這樣的例子也很多,例如,電視機遙控器(命令發送者)通過按鈕(具體命令)來遙控電視機(命令接收者),還有計算機鍵盤上的“功能鍵”等。

命令模式的定義與特點

命令(Command)模式的定義如下:將一個請求封裝爲一個對象,使發出請求的責任和執行請求的責任分割開。這樣兩者之間通過命令對象進行溝通,這樣方便將命令對象進行儲存、傳遞、調用、增加與管理。

命令模式的優點

  1. 將發起請求的對象與執行請求的對象解耦。
  2. 容易實現對請求的撤銷和重做
  3. 增加或刪除命令非常方便。採用命令模式增加與刪除命令不會影響其他類,它滿足“開閉原則”,對擴展比較靈活。
  4. 容易設計一個命令隊列。只要把命令對象放到列隊,就可以多線程的執行命令

缺點:可能產生大量具體命令類。因爲計對每一個具體操作都需要設計一個具體命令類,這將增加系統的複雜性。

命令模式的原理類圖與角色說明

命令模式的原理類圖

角色說明

  1. Invoker: 是請求的發送者,它通常擁有很多的命令對象,並通過訪問命令對象來執行相關請求,它不直接訪問接收者。
  2. Command: 聲明執行命令的接口,擁有執行命令的抽象方法 execute()。
  3. Receiver: 實現者/接受者角色,執行命令功能的相關操作,是具體命令對象業務的真正實現者。
  4. ConcreteCommand: 是抽象命令類的具體實現類,將一個接受者對象與一個動作綁定,調用接受者相應的操作,實現 execute。

命令模式的具體實現

以遙控器爲實例運用命令模式

Command(執行命令的接口)
//創建命令接口
public interface Command {
    //執行動作(操作)
    public void execute();
    //撤銷動作(操作)
    public void undo();
}
ConcreteCommand(抽象命令類的具體實現類)
//開燈命令
public class LightOffCommand implements Command {
    // 聚 合 LightReceiver
    LightReceiver light;
    // 構造器
    public LightOffCommand(LightReceiver light) { super();
        this.light = light;
    }

    @Override
    public void execute() {
        // 調用接收者的方法
        light.off();
    }

    @Override
    public void undo() {
        // 調用接收者的方法
        light.on();
    }
}
//關燈命令
public class LightOnCommand implements Command {
    // 聚 合 LightReceiver
    LightReceiver light;
    // 構造器
    public LightOnCommand(LightReceiver light) { super();
        this.light = light;
    }

    @Override
    public void execute() {
        // 調用接收者的方法
        light.on();
    }

    @Override
    public void undo() {
        // 調用接收者的方法
        light.off();
    }
}
/**
 * 	沒有任何命令,即空執行: 用於初始化每個按鈕, 當調用空命令時,對象什麼都不做
 * 	其實,這樣是一種設計模式, 可以省掉對空判斷
 */
public class NoCommand implements Command {
    @Override
    public void execute() {

    }

    @Override
    public void undo() {

    }
}
Receiver(實現者/接受者)
//開關燈的具體實現
public class LightReceiver {
    public void on() {
        System.out.println(" 電燈打開了.. ");
    }

    public void off() {
        System.out.println(" 電燈關閉了.. ");
    }
}
Invoker(請求的發送者)
//遙控器類
public class RemoteController {
    // 開 按鈕的命令數組
    Command[] onCommands;
    Command[] offCommands;

    // 執行撤銷的命令
    Command undoCommand;

    // 構造器,完成對按鈕初始化
    public RemoteController(){
        onCommands = new Command[5];
        offCommands = new Command[5];

        for (int i = 0; i < 5; i++) {
            onCommands[i] = new NoCommand();
            offCommands[i] = new NoCommand();
        }
    }

    // 給我們的按鈕設置你需要的命令
    public void setCommand(int no, Command onCommand, Command offCommand) {
        onCommands[no] = onCommand;
        offCommands[no] = offCommand;
    }

    // 按下開按鈕
    public void onButtonWasPushed(int no) { // no 0
        // 找到你按下的開的按鈕, 並調用對應方法
        onCommands[no].execute();
        // 記錄這次的操作,用於撤銷
        undoCommand = onCommands[no];
    }

    // 按下開按鈕
    public void offButtonWasPushed(int no) { // no 0
        // 找到你按下的關的按鈕, 並調用對應方法
        offCommands[no].execute();
        // 記錄這次的操作,用於撤銷
        undoCommand = offCommands[no];
    }

    // 按下撤銷按鈕
    public void undoButtonWasPushed() {
        undoCommand.undo();
    }
}
客戶端調用
public class Client {
    public static void main(String[] args) {
        //使用命令設計模式,完成通過遙控器,對電燈的操作

        //創建電燈的對象(接受者)
        LightReceiver lightReceiver = new LightReceiver();

        //創建電燈相關的開關命令
        LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver);
        LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver);

        //需要一個遙控器
        RemoteController remoteController = new RemoteController();

        //給我們的遙控器設置命令, 比如 no = 0  是電燈的開和關的操作
        remoteController.setCommand(0, lightOnCommand, lightOffCommand);

        System.out.println("--------按下燈的開按鈕-----------");
        remoteController.onButtonWasPushed(0);
        System.out.println("--------按下燈的關按鈕-----------");
        remoteController.offButtonWasPushed(0);
        System.out.println("--------按下撤銷按鈕-----------");
        remoteController.undoButtonWasPushed();
    }
}
/*
輸出結果:
--------按下燈的開按鈕-----------
 電燈打開了.. 
--------按下燈的關按鈕-----------
 電燈關閉了.. 
--------按下撤銷按鈕-----------
 電燈打開了.. 
*/

注:
上述的NoCommand類是一個空命令,空命令也是一種設計模式,它爲我們省去了判空的操作。在上面的實例中,如果沒有用空命令,我們每按下一個按鍵都要判空,這給我們編碼帶來一定的麻煩。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章