Command(CoR)模式也叫命令模式,是由GoF提出的23種軟件設計模式的一種。本文介紹設計模式中的(Command)模式的概念,用法,並用Command模式給出了一個簡單的execute/undo實現。
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抽象類。
ConcreteCommand
Command的具體實現類。
Receiver
需要被調用的目標對象。
Invorker
通過Invorker執行Command對象。
Client
調用方。
下面,我們使用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
這個類在我們的例裏沒有被用到,但我們仍給出了它的一個參考實現,不過用註釋表示它不可用
- 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();
- }
- }
- */
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>java Client
--- execute command ---
aaabbbcccddd
-- undo ---
aaabbb
C:/Command>
我們可以看到,使用Command模式非常簡單地就實現了undo操作。