設計模式之Command - 命令模式

Command(CoR)模式也叫命令模式,是由GoF提出的23種軟件設計模式的一種。本文介紹設計模式中的(Command)模式的概念,用法,並用Command模式給出了一個簡單的execute/undo實現。

 

Command模式是行爲模式之一,Command模式通過被稱爲Command的類封裝了對目標對象的調用行爲以及調用參數。

Command模式的應用場景

在面向對象的程序設計中,一個對象調用另一個對象,一般情況下的調用過程是:創建目標對象實例;設置調用參數;調用目標對象的方法。
但在有些情況下有必要使用一個專門的類對這種調用過程加以封裝,我們把這種專門的類稱作command類。
- 整個調用過程比較繁雜,或者存在多處這種調用。這時,使用Command類對該調用加以封裝,便於功能的再利用。
- 調用前後需要對調用參數進行某些處理。
- 調用前後需要進行某些額外處理,比如日誌,緩存,記錄歷史操作等。


一般來說,Command模式通常可應用到以下場景:
Multi-level undo(多級undo操作)
如果系統需要實現多級回退操作,這時如果所有用戶的操作都以command對象的形式實現,系統可以簡單地用stack來保存最近執行的命令,如果用戶需要執行undo操作,系統只需簡單地popup一個最近的command對象然後執行它的undo()方法既可。
Transactional behavior(原子事務行爲)
藉助command模式,可以簡單地實現一個具有原子事務的行爲。當一個事務失敗時,往往需要回退到執行前的狀態,可以藉助command對象保存這種狀態,簡單地處理回退操作。
Progress bars(狀態條)
假如系統需要按順序執行一系列的命令操作,如果每個command對象都提供一個getEstimatedDuration()方法,那麼系統可以簡單地評估執行狀態並顯示出合適的狀態條。
Wizards(導航)
通常一個使用多個wizard頁面來共同完成一個簡單動作。一個自然的方法是使用一個command對象來封裝wizard過程,該command對象在第一個wizard頁面顯示時被創建,每個wizard頁面接收用戶輸入並設置到該command對象中,當最後一個wizard頁面用戶按下“Finish”按鈕時,可以簡單地觸發一個事件調用execute()方法執行整個動作。通過這種方法,command類不包含任何跟用戶界面有關的代碼,可以分離用戶界面與具體的處理邏輯。
GUI buttons and menu items(GUI按鈕與菜單條等等)
Swing系統裏,用戶可以通過工具條按鈕,菜單按鈕執行命令,可以用command對象來封裝命令的執行。
Thread pools(線程池)
通常一個典型的線程池實現類可能有一個名爲addTask()的public方法,用來添加一項工作任務到任務隊列中。該任務隊列中的所有任務可以用command對象來封裝,通常這些command對象會實現一個通用的接口比如java.lang.Runnable。
Macro recording(宏紀錄)
可以用command對象來封裝用戶的一個操作,這樣系統可以簡單地通過隊列保存一系列的command對象的狀態就可以記錄用戶的連續操作。這樣通過執行隊列中的command對象,就可以完成“Play back”操作了。
Networking
通過網絡發送command命令到其他機器上運行。
Parallel Processing(併發處理)
當一個調用共享某個資源並被多個線程併發處理時。

等等

Command模式結構圖如下:

設計模式學習筆記(十四)—Command模式



Command
    Command抽象類。
ConcreteCommand
    Command的具體實現類。
Receiver
    需要被調用的目標對象。
Invorker
    通過Invorker執行Command對象。
Client
    調用方。

Command模式的應用範例

下面,我們使用Command模式實現一個簡單的execute/undo操作。
在該範例中,我們有一個簡單的操作:對字符串做append操作,這個操作由Receiver類實現;另外,我們需要記錄操作歷史,並能簡單加以回退(undo),所以我們採用Command模式實現。
文件一覽:
Client
    測試類
Command
    Command抽象類
UndoableCommand
    支持undo操作的Command抽象類,該類是Command類的子類
ConcreteCommand
    具體的UndoableCommand實現類,該類繼承UndoableCommand類,所以支持undo操作
CommandManager
    Command管理類。該類使用Stack來管理執行過的Command對象,並提供executeCommand()與undoCommand()方法
Receiver
    執行任務的目標類
Invoker
    這個類在我們的例裏沒有被用到,但我們仍給出了它的一個參考實現,不過用註釋表示它不可用

  1. import java.util.Stack;   
  2.   
  3. public class Client {   
  4.   
  5.     /**  
  6.      * Test Command Pattern  
  7.      *   
  8.      */  
  9.     public static void main(String[] args) {   
  10.         CommandManager commandMgr = new CommandManager();   
  11.            
  12.         Receiver receiver = new Receiver();   
  13.   
  14.         System.out.println("--- execute command ---");   
  15.         Command commandAaa = new ConcreteCommand(receiver, "aaa");   
  16.         commandMgr.executeCommand(commandAaa);   
  17.            
  18.         Command commandBbb = new ConcreteCommand(receiver, "bbb");   
  19.         commandMgr.executeCommand(commandBbb);   
  20.            
  21.         Command commandCcc = new ConcreteCommand(receiver, "ccc");   
  22.         commandMgr.executeCommand(commandCcc);   
  23.            
  24.         Command commandDdd = new ConcreteCommand(receiver, "ddd");   
  25.         commandMgr.executeCommand(commandDdd);   
  26.            
  27.         System.out.println(receiver.getData());   
  28.            
  29.         System.out.println("-- undo ---");   
  30.         commandMgr.undoCommand();   
  31.         commandMgr.undoCommand();   
  32.         System.out.println(receiver.getData());   
  33.     }   
  34. }   
  35.   
  36.   
  37. /**  
  38.  * Command  
  39.  * abstract command class  
  40.  *  
  41.  */  
  42. abstract class Command {   
  43.     protected Receiver receiver;   
  44.   
  45.     protected String param;   
  46.   
  47.     public Command(Receiver receiver, String expr) {   
  48.         this.receiver = receiver;   
  49.         this.param = expr;   
  50.     }   
  51.   
  52.     abstract public void execute();   
  53. }   
  54.   
  55. /**  
  56.  * UndoableCommand  
  57.  * abstract undo supportable command class which extends from Command class  
  58.  *  
  59.  */  
  60. abstract class UndoableCommand extends Command {   
  61.   
  62.     public UndoableCommand(Receiver receiver, String expr) {   
  63.         super(receiver, expr);   
  64.     }   
  65.   
  66.     abstract public void undo();   
  67. }   
  68.   
  69.   
  70. /**  
  71.  * ConcreteCommand  
  72.  * concrete command class which extends from UndoableCommand  
  73.  *  
  74.  */  
  75. class ConcreteCommand extends UndoableCommand {   
  76.     private String previousData = null;   
  77.   
  78.     public ConcreteCommand(Receiver receiver, String expr) {   
  79.         super(receiver, expr);   
  80.     }   
  81.   
  82.     @Override  
  83.     public void execute() {   
  84.         previousData = receiver.getData();   
  85.         receiver.append(this.param);   
  86.     }   
  87.   
  88.     @Override  
  89.     public void undo() {   
  90.         receiver.setData(previousData);   
  91.     }   
  92. }   
  93.   
  94.   
  95. /**  
  96.  * CommandManager  
  97.  * Command Manager class which stack the exe  
  98.  *  
  99.  */  
  100. class CommandManager {   
  101.     private Stack commandStack = new Stack();   
  102.   
  103.     public void executeCommand(Command cmd) {   
  104.         cmd.execute();   
  105.         if (cmd instanceof UndoableCommand) {   
  106.             commandStack.push(cmd);   
  107.         }   
  108.     }   
  109.   
  110.     public void undoCommand() {   
  111.         if (commandStack.size() > 0) {   
  112.             UndoableCommand cmd = (UndoableCommand) commandStack.pop();   
  113.             cmd.undo();   
  114.         } else {   
  115.             throw new UnsupportedOperationException("");   
  116.         }   
  117.     }   
  118. }   
  119.   
  120.   
  121. /**  
  122.  * Receiver  
  123.  * target object  
  124.  *  
  125.  */  
  126. class Receiver {   
  127.     private String data = "";   
  128.   
  129.     public void append(String expr) {   
  130.         data += expr;   
  131.     }   
  132.   
  133.     public String getData() {   
  134.         return data;   
  135.     }   
  136.   
  137.     public void setData(String data) {   
  138.         this.data = data;   
  139.     }   
  140. }   
  141.   
  142. /**  
  143.  
  144. class Invoker {  
  145.     private Command command;  
  146.  
  147.     public void setCommand(Command command) {  
  148.         this.command = command;  
  149.     }  
  150.  
  151.     public void executeCommand() {  
  152.         command.execute();  
  153.     }  
  154. }  
  155.  
  156.  */  
import java.util.Stack;

public class Client {

    /**
     * Test Command Pattern
     * 
     */
    public static void main(String[] args) {
        CommandManager commandMgr = new CommandManager();
        
        Receiver receiver = new Receiver();

        System.out.println("--- execute command ---");
        Command commandAaa = new ConcreteCommand(receiver, "aaa");
        commandMgr.executeCommand(commandAaa);
        
        Command commandBbb = new ConcreteCommand(receiver, "bbb");
        commandMgr.executeCommand(commandBbb);
        
        Command commandCcc = new ConcreteCommand(receiver, "ccc");
        commandMgr.executeCommand(commandCcc);
        
        Command commandDdd = new ConcreteCommand(receiver, "ddd");
        commandMgr.executeCommand(commandDdd);
        
        System.out.println(receiver.getData());
        
        System.out.println("-- undo ---");
        commandMgr.undoCommand();
        commandMgr.undoCommand();
        System.out.println(receiver.getData());
    }
}


/**
 * Command
 * abstract command class
 *
 */
abstract class Command {
    protected Receiver receiver;

    protected String param;

    public Command(Receiver receiver, String expr) {
        this.receiver = receiver;
        this.param = expr;
    }

    abstract public void execute();
}

/**
 * UndoableCommand
 * abstract undo supportable command class which extends from Command class
 *
 */
abstract class UndoableCommand extends Command {

    public UndoableCommand(Receiver receiver, String expr) {
        super(receiver, expr);
    }

    abstract public void undo();
}


/**
 * ConcreteCommand
 * concrete command class which extends from UndoableCommand
 *
 */
class ConcreteCommand extends UndoableCommand {
    private String previousData = null;

    public ConcreteCommand(Receiver receiver, String expr) {
        super(receiver, expr);
    }

    @Override
    public void execute() {
        previousData = receiver.getData();
        receiver.append(this.param);
    }

    @Override
    public void undo() {
        receiver.setData(previousData);
    }
}


/**
 * CommandManager
 * Command Manager class which stack the exe
 *
 */
class CommandManager {
    private Stack commandStack = new Stack();

    public void executeCommand(Command cmd) {
        cmd.execute();
        if (cmd instanceof UndoableCommand) {
            commandStack.push(cmd);
        }
    }

    public void undoCommand() {
        if (commandStack.size() > 0) {
            UndoableCommand cmd = (UndoableCommand) commandStack.pop();
            cmd.undo();
        } else {
            throw new UnsupportedOperationException("");
        }
    }
}


/**
 * Receiver
 * target object
 *
 */
class Receiver {
    private String data = "";

    public void append(String expr) {
        data += expr;
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }
}

/**

class Invoker {
    private Command command;

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

    public void executeCommand() {
        command.execute();
    }
}

 */




執行Client,輸出結果:

C:/Command>javac *.java
C:/Command>java Client
--- execute command ---
aaabbbcccddd
-- undo ---
aaabbb
C:/Command>

我們可以看到,使用Command模式非常簡單地就實現了undo操作。

發佈了14 篇原創文章 · 獲贊 2 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章