1. 程式人生 > >設計模式 —— 命令模式(Command Pattern)

設計模式 —— 命令模式(Command Pattern)

命令模式(Command Pattern)

概念:

概述:在軟體設計中,我們經常會遇到某些物件傳送請求,然後某些物件接受請求後執行,但傳送請求的物件可能並不知道接受請求的物件是誰,執行的是什麼動作。此時可通過 命令模式 來實現,讓傳送者和接受者完全的鬆耦合,這樣可大大增強程式的靈活性。

定義:命令模式 將“請求”封裝成物件,以便使用不同的請求,佇列或者日誌來引數化其他物件。命令模式也支援可撤銷的操作。

組成:

命令模式

Client(客戶):負責建立一個具體的命令(Concrete Command)
Invoker(呼叫者):呼叫者持有一個命令物件,並在某個時刻呼叫命令物件的 execute() 方法。
Command(命令介面):包含命令物件的 execute() 方法和 undo() 方法。
ConcreteCommand(具體命令):實現命令介面。包括兩個操作,執行命令和撤銷命令。
Receiver(接收者):接受命令並執行。

例子:

現在比較火的小米手機,可以當作遙控器控制多種不同的家電,手機發送命令,不同的電器接收到後執行。

命令模式

Command 類

public interface Command {
    public void execute();
    public void undo();
}

開燈命令

public class LightOnCommand implements Command {
    private Light light;

    LightOnCommand(Light light) {
        this.light = light;
    }

    public
void execute() { light.on(); } public void undo() { light.off(); } }

開電視命令

public class TVOnCommand implements Command {
    private TV tv;

    public TVOnCommand(TV tv) {
        this.tv = tv;
    }

    public void execute() {
        tv.on();
    }

    public
void undo() { tv.off(); } }

家用電器介面類

public interface HouseholdAppliances {
    public void on();
    public void off();
}

電視類

public class TV implements HouseholdAppliances {
    public void on() {
        System.out.println("the TV on");
    }

    public void off() {
        System.out.println("the TV off");
    }
}

電燈類

public class Light implements HouseholdAppliances{
    public void on() {
        System.out.println("the light on");
    }

    public void off() {
        System.out.println("the light off");
    }
}

手機控制器類

public class MiPhone {
    ArrayList commands;

    public MiPhone() {
        commands = new ArrayList();
    }

    public void setCommand(Command command) {
        commands.add(command);
    }

    public void onButtonWasPushed(int slot) {
        ((Command)commands.get(slot-1)).execute();
    }

    public static void main(String[] args) {
        MiPhone miPhone = new MiPhone();
        //建立電器
        Light light = new Light();
        TV tv = new TV();
        //建立命令
        LightOnCommand lightOnCommand = new LightOnCommand(light);
        TVOnCommand tvOnCommand = new TVOnCommand(tv);
        //給小米手機設定命令
        //設定第一個按鈕為開燈
        miPhone.setCommand(lightOnCommand);
        //設定第二個按鈕為開電視
        miPhone.setCommand(tvOnCommand);

        //開燈
        miPhone.onButtonWasPushed(1);
        //開電視
        miPhone.onButtonWasPushed(2);
    }
}

執行結果:

命令模式

適用場景

  • 系統需要將請求呼叫者和請求接收者解耦,使得呼叫者和接收者不直接互動。
  • 系統需要在不同的時間指定請求、將請求排隊(如:執行緒池+工作佇列)和執行請求。
  • 系統需要支援命令的撤銷(Undo)操作和恢復(Redo)操作。
  • 系統需要將一組操作組合在一起,即支援巨集命令。

優缺點:

優點:

  • 降低系統的耦合度:Command模式將呼叫操作的物件與知道如何實現該操作的物件解耦。
  • Command是頭等的物件。它們可像其他的物件一樣被操縱和擴充套件。
  • 組合命令:你可將多個命令裝配成一個組合命令,即可以比較容易地設計一個命令佇列和巨集命令。一般說來,組合命令是Composite模式的一個例項。
  • 增加新的Command很容易,因為這無需改變已有的類。
  • 可以方便地實現對請求的Undo和Redo。

缺點:

  • 使用命令模式可能會導致某些系統有過多的具體命令類。因為針對每一個命令都需要設計一個具體命令類,因此某些系統可能需要大量具體命令類,這將影響命令模式的使用。

更多用途:

  • 巨集命令模式:命令模式 加 組合模式,我們可以將多個命令組合到一起來實現命令的批處理。
  • 佇列請求:將命令排成一個佇列打包,一個個呼叫 execute 方法,如執行緒池的任務佇列,執行緒不關心任務佇列中是讀 IO 還是計算,只取出命令後執行,接著進行下一個。
  • 日誌請求:某些應用需要我們將所有的動作記錄在日誌中,然後在系統宕機等情況出現時,重新呼叫這些動作恢復到之前的狀態。如資料庫事務。

參考:

《Head first 設計模式》