設計模式-指令模式
1.問題引出
智能生活項目需求
- 我們買了一套智能家電,有照明燈、風扇、冰箱、洗衣機,我們隻要在手機上安裝app就可以控制對這些家電工作。
- 這些智能家電來自不同的廠家,我們不想針對每一種家電都安裝一個App,分别控制,我們希望隻要一個app就可以控制全部智能家電。
- 要實作一個app控制所有智能家電的需要,則每個智能家電廠家都要提供一個統一的接口給app調用,這時 就可以考慮使用指令模式。
- 指令模式可将“動作的請求者”從“動作的執行者”對象中解耦出來.
- 在我們的例子中,動作的請求者是手機app,動作的執行者是每個廠商的一個家電産品
- 指令模式(Command Pattern):在軟體設計中,我們經常需要向某些對象發送請求,但是并不知道請求的接收者是誰,也不知道被請求的操作是哪個,我們隻需在程式運作時指定具體的請求接收者即可,此時,可以使用指令模式來進行設計
- 命名模式使得請求發送者與請求接收者消除彼此之間的耦合,讓對象之間的調用關系更加靈活,實作解耦。
- 在命名模式中,會将一個請求封裝為一個對象,以便使用不同參數來表示不同的請求(即命名),同時指令模式也支援可撤銷的操作。
- 通俗易懂的了解:将軍釋出指令,士兵去執行。其中有幾個角色:将軍(指令釋出者)、士兵(指令的具體執行者)、指令(連接配接将軍和士兵)。Invoker是調用者(将軍),Receiver是被調用者(士兵),MyCommand是指令,實作了Command接口,持有接收對象
package command;
public interface Command {
//執行動作(操作)
public void execute();
//撤銷動作(操作)
public void undo();
}
package command;
public class LightOnCommand implements Command{
LightReceive light;
//構造器
public LightOnCommand(LightReceive light) {
this.light = light;
}
@Override
public void execute() {
//調用接收者的方法
light.on();
}
@Override
public void undo() {
//調用接收者的方法
light.off();
}
}
package command;
public class LightOffCommand implements Command{
LightReceive light;
//構造器
public LightOffCommand(LightReceive light) {
this.light = light;
}
@Override
public void execute() {
//調用接收者的方法
light.off();
}
@Override
public void undo() {
//調用接收者的方法
light.on();
}
}
package command;
public class NoCommand implements Command{
@Override
public void execute() {
}
@Override
public void undo() {
}
}
package command;
public class LightReceive {
public void on(){
System.out.println("電燈打開了");
}
public void off(){
System.out.println("電燈關閉了");
}
}
package command;
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){
//找到你按下的開的按鈕,并調用對應的方法
onCommands[no].execute();
//記錄這次的操作,用于撤銷
undoCommand = onCommands[no];
}
//按下關按鍵
public void offButtonWasPushed(int no){
//找到你按下的關的按鈕,并調用對應的方法
offCommands[no].execute();
//記錄這次的操作,用于撤銷
undoCommand = offCommands[no];
}
//按下撤銷按鍵
public void undoButtonWasPushed(int no){
undoCommand.undo();
}
}
package command;
public class Client {
public static void main(String[] args) {
//建立電燈的對象(接受者)
LightReceive lightReceive = new LightReceive();
//建立電燈相關的開關指令
LightOnCommand lightOnCommand = new LightOnCommand(lightReceive);
LightOffCommand lightOffCommand = new LightOffCommand(lightReceive);
//需要一個遙控器
RemoteController remoteController = new RemoteController();
//給我們的遙控器設定指令
remoteController.setCommand(0,lightOnCommand,lightOffCommand);
//按下按鈕
remoteController.onButtonWasPushed(0);
remoteController.offButtonWasPushed(0);
remoteController.undoButtonWasPushed(0);
}
}
- 将發起請求的對象與執行請求的對象解耦。發起請求的對象是調用者,調用者隻要調用指令對象的execute()方法就可以讓接收者工作,而不必知道具體的接收者對象是誰、是如何實作的,指令對象會負責讓接收者執行請求的動作,也就是說:”請求發起者”和“請求執行者”之間的解耦是通過指令對象實作的,指令對象起到了紐帶橋梁的作用。
- 容易設計一個指令隊列。隻要把指令對象放到列隊,就可以多線程的執行指令
- 容易實作對請求的撤銷和重做
- 指令模式不足:可能導緻某些系統有過多的具體指令類,增加了系統的複雜度,這點在在使用的時候要注意
- 空指令也是一種設計模式,它為我們省去了判空的操作。在上面的執行個體中,如果沒有用空指令,我們每按下一個按鍵都要判空,這給我們編碼帶來一定的麻煩。
- 指令模式經典的應用場景:界面的一個按鈕都是一條指令、模拟CMD(DOS指令)訂單的撤銷/恢複、觸發-回報機制