1 概述
命令模式(Command Pattern),是將請求封裝成一系列命令對象,以解耦請求發起者和請求接收者的一種行爲模式。
2 命令模式
正常情況下,請求發送者和請求接收者是相互耦合的,發送者直接調用接收者相關的方法,直接交互。
用命令模式進行改造,將兩者交互的媒介----命令單獨抽離出來,使得發送者和接收者相互獨立,只依賴於命令對象,提高了擴展性和可維護性。同時,通過命令模式,我們還能完成一些高級操作,如撤銷命令,方法參數化,命令入隊,批量命令等等。
3 案例
通過一個簡單的例子加深對命令模式的理解。小時候我們都玩過遙控賽車,賽車可以根據遙控器的按鈕,執行相應的動作。我們作爲命令的發送者,其實並不直接跟賽車交互,而是通過遙控器的指令來操作賽車:
public interface RemoteControlCar {
void moveForward();
void turnLeft();
void turnRight();
void moveBackward();
}
//請求接收者
public class RemoteControlCarImpl implements RemoteControlCar {
@Override
public void moveForward() {
System.out.println("The car is moving forward!");
}
@Override
public void turnLeft() {
System.out.println("The car turns left!");
}
@Override
public void turnRight() {
System.out.println("The car turns right!");
}
@Override
public void moveBackward() {
System.out.println("The car is moving backward!");
}
}
//命令接口,支持回退操作
public interface RemoteControlCommand {
void execute();
void undo();
}
public class ForwardCommand implements RemoteControlCommand {
RemoteControlCar rcCar;
public ForwardCommand(RemoteControlCar rcCar) {
this.rcCar = rcCar;
}
@Override
public void execute() {
rcCar.moveForward();
}
@Override
public void undo() {
rcCar.moveBackward();
}
}
public class BackwardCommand implements RemoteControlCommand {
RemoteControlCar rcCar;
public BackwardCommand(RemoteControlCar rcCar) {
this.rcCar = rcCar;
}
@Override
public void execute() {
rcCar.moveBackward();
}
@Override
public void undo() {
rcCar.moveForward();
}
}
public class TurnLeftCommand implements RemoteControlCommand {
RemoteControlCar rcCar;
public TurnLeftCommand(RemoteControlCar rcCar) {
this.rcCar = rcCar;
}
@Override
public void execute() {
rcCar.turnLeft();
}
@Override
public void undo() {
rcCar.turnRight();
}
}
public class TurnRightCommand implements RemoteControlCommand {
RemoteControlCar rcCar;
public TurnRightCommand(RemoteControlCar rcCar) {
this.rcCar = rcCar;
}
@Override
public void execute() {
rcCar.turnRight();
}
@Override
public void undo() {
rcCar.turnLeft();
}
}
public interface CarPlayer {
void play();
void undo();
void setCommand(RemoteControlCommand command);
}
// 命令發送者,將請求委託給命令對象來完成。通過設置不同的命令,完成不同的操作
public class Kid implements CarPlayer {
RemoteControlCommand command;
public Kid(RemoteControlCommand command) {
this.command = command;
}
@Override
public void setCommand(RemoteControlCommand command) {
this.command = command;
}
@Override
public void play() {
command.execute();
}
@Override
public void undo() {
command.undo();
}
}
public class Test {
public static void main(String[] args) {
RemoteControlCar remoteControlCar = new RemoteControlCarImpl();
RemoteControlCommand moveCommand = new ForwardCommand(remoteControlCar);
RemoteControlCommand turnLeftCommand = new TurnLeftCommand(remoteControlCar);
RemoteControlCommand turnRightCommand = new TurnRightCommand(remoteControlCar);
RemoteControlCommand stopCommand = new BackwardCommand(remoteControlCar);
// 需要完成不同的操作,只需設置不同的命令
CarPlayer kid = new Kid(moveCommand);
kid.play();
kid.undo();
kid.setCommand(turnLeftCommand);
kid.play();
kid.setCommand(turnRightCommand);
kid.play();
kid.setCommand(stopCommand);
kid.play();
kid.undo();
}
}
輸出:
The car is moving forward!
The car is moving backward!
The car turns left!
The car turns right!
The car is moving backward!
The car is moving forward!
在上述例子中,我們避免了CarPlayer
和RemoteControlCar
的直接交互,將所有的複雜度都放在RemoteControlCommand
中。對命令的增刪改並不影響整體系統。在這裏只演示了通過命令模式可以完成撤銷操作;不過很容易聯想,將幾個命令放到一個命令集合中一起調用,便可以達到批量操作的目的;將命令放入隊列,用類似生產者–消費者的模式,可以做到延遲命令/命令入隊的要求。
JDK
中我們熟悉線程,就是命令模式的運用。Runnable相當於是Thread對象的命令。
4 總結
命令對象是命令模式的核心,它像是一個橋樑,連接了請求調用者和接收者,並解耦了兩者的關係。它使得系統擴展變得容易。