在軟件開發系統中,常常出現“方法的請求者”與“方法的實現者”之間存在緊密的耦合關係。這不利於軟件功能的擴展與維護。例如,想對行爲進行“撤銷、重做、記錄”等處理都很不方便,因此“如何將方法的請求者與方法的實現者解耦?”變得很重要,命令模式能很好地解決這個問題。
在現實生活中,這樣的例子也很多,例如,電視機遙控器(命令發送者)通過按鈕(具體命令)來遙控電視機(命令接收者),還有計算機鍵盤上的“功能鍵”等。
命令模式的定義與特點
命令(Command)模式的定義如下:將一個請求封裝爲一個對象,使發出請求的責任和執行請求的責任分割開。這樣兩者之間通過命令對象進行溝通,這樣方便將命令對象進行儲存、傳遞、調用、增加與管理。
命令模式的優點:
- 將發起請求的對象與執行請求的對象解耦。
- 容易實現對請求的撤銷和重做
- 增加或刪除命令非常方便。採用命令模式增加與刪除命令不會影響其他類,它滿足“開閉原則”,對擴展比較靈活。
- 容易設計一個命令隊列。只要把命令對象放到列隊,就可以多線程的執行命令
缺點:可能產生大量具體命令類。因爲計對每一個具體操作都需要設計一個具體命令類,這將增加系統的複雜性。
命
命令模式的原理類圖與角色說明
角色說明
- Invoker: 是請求的發送者,它通常擁有很多的命令對象,並通過訪問命令對象來執行相關請求,它不直接訪問接收者。
- Command: 聲明執行命令的接口,擁有執行命令的抽象方法 execute()。
- Receiver: 實現者/接受者角色,執行命令功能的相關操作,是具體命令對象業務的真正實現者。
- 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類是一個空命令,空命令也是一種設計模式,它爲我們省去了判空的操作。在上面的實例中,如果沒有用空命令,我們每按下一個按鍵都要判空,這給我們編碼帶來一定的麻煩。