15 命令模式

定義

命令模式(Command Pattern):將一個請求封裝爲一個對象,從而可用不同的請求對客戶端進行參數化,對請求排隊或者記錄請求日誌,以及支持可撤銷的操作。

結構

  1. Command(抽象命令類):抽象命令類一般是一個抽象類或接口,在其中聲明瞭用於執行請求的execute()等方法,通過這些方法可以調用請求接受者的相關操作。
  2. ConcreteCommand(具體命令類):具體命令類是抽象命令類的子類,實現了在抽象命令類中聲明的方法,它對應具體的接收者對象,將接受者對象的動作綁定其中。具體命令類在實現execute()方法時將調用接收者對象的相關操作(Action)。
  3. Invoker(調用者):調用者即請求發送者,它通過命令對象來執行請求。一個調用者並不需要在設計時確定其接收者,因此它只與抽象命令類之間存在關聯關係。在程序運行時可以將一個具體命令對象注入其中,再調用具體命令對象的execute()方法,從而實現間接調用請求接收者的相關操作。
  4. Receiver(接收者):接收者執行與請求相關的操作,具體實現對請求的業務處理。

代碼

Command

public abstract class Command {

    public abstract void execute();

}

Receiver

public class Receiver {

    public void action() {
        // 具體操作
        System.out.println("action");
    }

}

ConcreteCommand

public class ConcreteCommand extends Command {

    private Receiver receiver;

    public ConcreteCommand(Receiver receiver) {
        this.receiver = receiver;
    }

    @Override
    public void execute() {
        receiver.action();
    }

}

Invoker

public class Invoker {

    private Command command;

    public Invoker(Command command) {
        this.command = command;
    }

    // 業務方法,用於調用命令類的execute()
    public void call() {
        command.execute();
    }

    public void setCommand(Command command) {
        this.command = command;
    }

}

Test

public class Test {

    public static void main(String[] args) {
        Receiver receiver = new Receiver();
        Command command = new ConcreteCommand(receiver);
        Invoker invoker = new Invoker(command);
        invoker.call();
    }

}
時間命令隊列

有時候,當一個請求發送者發送一個請求時有不止一個請求接收者產生響應,這些請求接收者將逐個執行業務方法,完成對請求的處理,此時可以通過命令隊列來實現。

CommandQueue:命令隊列

import java.util.ArrayList;
import java.util.List;

public class CommandQueue {
    
    /** 定義一個List來存儲命令隊列 */
    private List<Command> commandList = new ArrayList<>();
    
    public void addCommand(Command command) {
        commandList.add(command);
    }
    
    public void removeCommand(Command command) {
        commandList.remove(command);
    }
    
    public void execue() {
        // 循環調用
        commandList.forEach(Command::execute);
    }
    
}

Invoker:請求發送者

public class Invoker1 {

    // 維持一個命令隊列的引用
    private CommandQueue commandQueue;

    // 構造注入
    public Invoker1(CommandQueue commandQueue) {
        this.commandQueue = commandQueue;
    }

    // 調用
    public void call() {
        commandQueue.execue();
    }

}
實現撤銷操作

在命令模式中可以通過對命令類進行修改使得系統支持(Undo)操作和恢復(Redo)操作,下面通過一個簡單實例來學習如何使用命令模式中實現撤銷操作。

設計一個簡易計算器,該計算器可以實現簡單的數學操作,還可以對運算結果實施撤銷操作。

Adder充當請求接收者

public class Adder {

    private int num;

    // 實現加法操作
    public int add(int value) {
        num += value;
        return num;
    }

}

AbstractCommand充當抽象命令,聲明瞭execute()和撤銷undo()

public abstract class AbstractorCommand {

    // 執行方法
    public abstract int execute(int value);
    // 撤銷
    public abstract int undo();

}

AddCommand充當具體抽象類

public class AddCommand extends AbstractorCommand {

    private Adder adder = new Adder();
    private int value;

    // 調用加法類的加法操作
    @Override
    public int execute(int value) {
        this.value = value;
        return adder.add(value);
    }

    // 加一個相反數實現加法的逆向操作
    @Override
    public int undo() {
        return adder.add(-value);
    }

}

CalculatorForm充當請求發送者

public class CalculatorForm {

    private AbstractorCommand command;

    public CalculatorForm(AbstractorCommand command) {
        this.command = command;
    }

    // 調用命令對象的execute()方法執行運算
    public void compute(int value) {
        int i = command.execute(value);
        System.out.println("運算結果:" + i);
    }

    public void undo() {
        int i = command.undo();
        System.out.println("撤銷操作,結果:" + i);
    }

}

客戶端調用

public class Client {

    public static void main(String[] args) {
        AbstractorCommand command = new AddCommand();
        CalculatorForm form = new CalculatorForm(command);
        form.compute(10);
        form.compute(5);
        form.compute(10);
        form.undo();
    }

}

運行結果

運算結果:10
運算結果:15
運算結果:25
撤銷操作,結果:15

優/缺點與適用環境

  1. 優點
  • 降低系統的耦合度。由於請求者與接收者之間不存在直接引用,因此請求者與接收者之間實現完全解耦,相同的請求者可以對應不同的接收者,同樣相同的接收者也可以供不同的請求者使用,兩者之間具有良好的獨立性。
  • 新的命令可以很容易地加入到系統中。由於增加新的具體命令類不會影響到其他類,因此增加新的具體命令類很容易,無須修改原有系統源代碼,滿足開閉原則的要求。
  • 可以較容易地設計一個命令隊列或宏命令(組合命令)。
  • 爲請求的撤銷(Undo)和恢復(Redo)操作提供了一種設計和實現方案。
  1. 缺點
  • 使用命令模式可能會導致系統有過多的具體命令類。因此針對每一請求接收者的調用操作都需要設計一個具體命令類,所以在某些系統中可能需要提供大量的具體命令類,這會影響命令模式的使用。
  1. 適用環境
  • 系統需要將請求調用者和請求接收者解耦,使得調用者和接收者不直接交互。請求調用者無需知道接收者的存在,與無序知道接收者是誰,接收者也無須關心何時被調用。
  • 系統需要在不同的時間指定請求、將請求排隊和執行請求。一個命令對象的請求的初始調用者可以有不同的生命週期,換而言之,最初的請求發送者可能已經不在了,而命令對象本身仍然是活動的,可以通過該命令對象去調用請求接收者,而且無需關心請求調用者的存在性,可以通過請求日誌文件等機制來實現具體操作。
  • 系統需要支持命令的撤銷(Undo)操作和恢復(Redo)操作
  • 系統需要將一組操作組合在一起形成宏命令。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章