命令模式又稱為行動/交易模式,屬于行為型模式;是指將一個請求封裝為一個物件,使發出請求的責任和執行請求的責任分割開,這樣兩者之間通過命令物件進行溝通,這樣方便將命令物件進行儲存、傳遞、呼叫、增加與管理,命令模式是對命令的封裝,命令模式把發出命令的責任和執行命令的責任分割開,委派給不同的物件,
命令模式的UML類圖如下:

從上圖可知,命令模式涉及到抽象命令角色、具體命令角色、請求者角色、接收者角色、客戶端角色等五個角色:
- 命令(Command)角色:宣告了一個給所有具體命令類的抽象介面,這是一個抽象角色,通常由一個Java介面或Java抽象類實作,
- 具體命令(ConcreteCommand)角色:定義一個接收者和行為之間的弱耦合;實作execute()方法,負責呼叫接收者的相應操作,execute()方法通常叫做執行方法,
- 請求者(Invoker)角色:負責呼叫命令物件執行請求,相關的方法叫做行動萬法,
- 接收者(Receiver)角色:負責具體實施和執行一個請求,任何一個類都可以成為接收者,實施和執行請求的方法叫做行動方法,
- 客戶(Client)角色:創建了一個具體命令(ConcreteCommand)物件并確定其接收者,
命令模式的執行順序如下:
- 客戶端創建一個ConcreteCommand物件,并指明接收者
- 請求者物件保存了ConcreteCommand物件
- 請求者物件通過呼叫execute()方法發出請求,如果命令是能撤銷的,那么ConcreteCommand保存了呼叫execute()方法之前的狀態,
- ConcreteCommand物件呼叫接受者一方的方法執行請求
智能家電的例子
現在物聯網發展越來越快,智能家電發展越來越迅速,接下來用一個移動APP管理智能家電,通過移動管理電燈、電視機的打開為例講解命令模式,
智能家電的UML類圖:

接收者角色:
package com.charon.command;
/**
* @className: LightReceiver
* @description: 命令接收者
* @author: charon
* @create: 2022-03-26 22:13
*/
public class LightReceiver {
public void on(){
System.out.println("電燈打開了,,,,");
}
public void off(){
System.out.println("電燈關閉了,,,,");
}
}
抽象命令角色:
package com.charon.command;
/**
* @className: Command
* @description: 命令介面
* @author: charon
* @create: 2022-03-26 22:12
*/
public interface Command {
/**
* 執行操作
*/
void execute();
/**
* 撤銷操作
*/
void undo();
}
具體命令角色:
package com.charon.command;
/**
* @className: LightOnCommand
* @description:
* @author: charon
* @create: 2022-03-26 22:15
*/
public class LightOnCommand implements Command{
private LightReceiver light;
public LightOnCommand(LightReceiver receiver) {
this.light = receiver;
}
/**
* 重寫執行方法
*/
@Override
public void execute() {
light.on();
}
@Override
public void undo() {
light.off();
}
}
package com.charon.command;
/**
* @className: LightOffCommand
* @description:
* @author: charon
* @create: 2022-03-26 22:17
*/
public class LightOffCommand implements Command{
private LightReceiver light;
public LightOffCommand(LightReceiver light) {
this.light = light;
}
@Override
public void execute() {
light.off();
}
@Override
public void undo() {
light.on();
}
}
package com.charon.command;
/**
* @className: NoCommand
* @description: 空命令方法,用于初始化每個按鈕,當呼叫空命令時,物件什么都不做
* 這也是一種設計模式,可以省略掉對空的判斷
* @author: charon
* @create: 2022-03-26 22:19
*/
public class NoCommand implements Command {
@Override
public void execute() {
}
@Override
public void undo() {
}
}
命令控制類:
package com.charon.command;
/**
* @className: RemoteController
* @description: 命令控制類
* @author: charon
* @create: 2022-03-26 22:20
*/
public class RemoteController {
/**
* 開關按鈕的命令,宣告為陣列是為了擴展其他的命令
*/
Command[] onCmds,offCmds;
/**
* 撤銷的命令
*/
Command undoCmd;
/**
* 初始化
*/
public RemoteController() {
onCmds = new Command[5];
offCmds = new Command[5];
for (int i = 0; i < 5; i++) {
onCmds[i] = new NoCommand();
offCmds[i] = new NoCommand();
}
}
/**
* 設定命令
* @param no 命令編號
* @param onCmd 打開命令
* @param offCmd 關閉命令
*/
public void setCommand(int no,Command onCmd,Command offCmd){
onCmds[no] = onCmd;
offCmds[no] = offCmd;
}
/**
* 按下開的命令
* @param no 命令編號
*/
public void onButtonWasPushed(int no){
// 找到對應的編號,呼叫對應的方法
onCmds[no].execute();
// 記錄這次操作,用于撤銷操作
undoCmd = onCmds[no];
}
/**
* 按下關的命令
* @param no 命令編號
*/
public void offButtonWasPushed(int no){
// 找到對應的編號,呼叫對應的方法
offCmds[no].execute();
// 記錄這次操作,用于撤銷操作
undoCmd = offCmds[no];
}
/**
* 按下撤銷操作
*/
public void undoButtonWasPushed(){
undoCmd.undo();
}
}
測驗:
package com.charon.command;
/**
* @className: Client
* @description:
* @author: charon
* @create: 2022-03-26 22:30
*/
public class Client {
public static void main(String[] args) {
// 創建命令的接收者
LightReceiver lightReceiver = new LightReceiver();
// 創建開/關的命令
LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver);
LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver);
// 創建命令控制器
RemoteController controller = new RemoteController();
// 給遙控器設定命令,比如 0 是電燈的開關操作
controller.setCommand(0,lightOnCommand,lightOffCommand);
System.out.println("按下電燈的開按鈕:" );
controller.onButtonWasPushed(0);
System.out.println("按下電燈的關按鈕:" );
controller.offButtonWasPushed(0);
System.out.println("按下電燈的撤銷按鈕:" );
controller.undoButtonWasPushed();
}
}
列印:
按下電燈的開按鈕:
電燈打開了,,,,
按下電燈的關按鈕:
電燈關閉了,,,,
按下電燈的撤銷按鈕:
電燈打開了,,,,
如上面的代碼所示,命令模式就已經完成了,如果接下來需要添加一個智能電視機,就只需要添加命令接收者和具體的命令類就行了:
package com.charon.command;
/**
* @className: TVReceiver
* @description:
* @author: charon
* @create: 2022-03-26 22:48
*/
public class TVReceiver {
public void on(){
System.out.println("電視機打開了,,,,");
}
public void off(){
System.out.println("電視機關閉了,,,,");
}
}
package com.charon.command;
/**
* @className: TVOnCommand
* @description:
* @author: charon
* @create: 2022-03-26 22:49
*/
public class TVOnCommand implements Command{
private TVReceiver tv;
public TVOnCommand(TVReceiver tv) {
this.tv = tv;
}
@Override
public void execute() {
tv.on();
}
@Override
public void undo() {
tv.off();
}
}
package com.charon.command;
/**
* @className: TVOffCommand
* @description:
* @author: charon
* @create: 2022-03-26 22:50
*/
public class TVOffCommand implements Command{
private TVReceiver tv;
public TVOffCommand(TVReceiver tv) {
this.tv = tv;
}
@Override
public void execute() {
tv.off();
}
@Override
public void undo() {
tv.on();
}
}
測驗:
package com.charon.command;
/**
* @className: Client
* @description:
* @author: charon
* @create: 2022-03-26 22:30
*/
public class Client {
public static void main(String[] args) {
TVReceiver tvReceiver = new TVReceiver();
TVOnCommand tvOnCommand = new TVOnCommand(tvReceiver);
TVOffCommand tvOffCommand = new TVOffCommand(tvReceiver);
// 創建命令控制器
RemoteController remoteController = new RemoteController();
// 給遙控器設定命令,比如 0 是電燈的開關操作
remoteController.setCommand(0,lightOnCommand,lightOffCommand);
System.out.println("按下電視機的開按鈕:" );
remoteController.onButtonWasPushed(0);
System.out.println("按下電視機的關按鈕:" );
remoteController.offButtonWasPushed(0);
System.out.println("按下電視機的撤銷按鈕:" );
remoteController.undoButtonWasPushed();
System.out.println("按下電視機的開按鈕:" );
remoteController.offButtonWasPushed(0);
System.out.println("按下電視機的撤銷按鈕:" );
remoteController.undoButtonWasPushed();
}
}
列印:
按下電視機的開按鈕:
電燈打開了,,,,
按下電視機的關按鈕:
電燈關閉了,,,,
按下電視機的撤銷按鈕:
電燈打開了,,,,
按下電視機的開按鈕:
電燈關閉了,,,,
按下電視機的撤銷按鈕:
電燈打開了,,,,
命令模式的優點如下:
- 通過引入中間件(抽象介面)降低系統的耦合度,
- 擴展性良好,增加或洗掉命令非常方便,采用命令模式增加與洗掉命令不會影響其他類,且滿足“開閉原則”,
- 可以實作宏命令,命令模式可以與組合模式結合,將多個命令裝配成一個組合命令,即宏命令,
- 方便實作 Undo 和 Redo 操作,命令模式可以與后面將要介紹的備忘錄模式結合,實作命令的撤銷與恢復,
- 可以在現有命令的基礎上,增加額外功能,比如日志記錄,結合裝飾器模式會更加靈活,
命令模式的缺點如下:
- 可能產生大量具體的命令類,因為每一個具體操作都需要設計一個具體命令類,這會增加系統的復雜性,
- 命令模式的結果其實就是接收方的執行結果,但是為了以命令的形式進行架構、解耦請求與實作,引入了額外型別結構(引入了請求方與抽象命令介面),增加了理解上的困難,不過這也是設計模式的通病,抽象必然會額外增加類的數量,代碼抽離肯定比代碼聚合更加難理解,
命令模式的應用場景
當系統的某項操作具備命令語意,且命令實作不穩定(變化)時,可以通過命令模式解耦請求與實作,使用抽象命令介面使請求方的代碼架構穩定,封裝接收方具體命令的實作細節,接收方與抽象命令呈現弱耦合(內部方法無需一致),具備良好的擴展性,
命令模式通常適用于以下場景:
- 請求呼叫者需要與請求接收者解耦時,命令模式可以使呼叫者和接收者不直接互動,
- 系統隨機請求命令或經常增加、洗掉命令時,命令模式可以方便地實作這些功能,
- 當系統需要執行一組操作時,命令模式可以定義宏命令來實作該功能,
- 當系統需要支持命令的撤銷(Undo)操作和恢復(Redo)操作時,可以將命令物件存盤起來,采用備忘錄模式來實作,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/449800.html
標籤:其他
上一篇:設計模式之模板方法模式
