設計模式,行爲模式之命令模式

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!

在上述例子中,我們避免了CarPlayerRemoteControlCar的直接交互,將所有的複雜度都放在RemoteControlCommand中。對命令的增刪改並不影響整體系統。在這裏只演示了通過命令模式可以完成撤銷操作;不過很容易聯想,將幾個命令放到一個命令集合中一起調用,便可以達到批量操作的目的;將命令放入隊列,用類似生產者–消費者的模式,可以做到延遲命令/命令入隊的要求。

JDK中我們熟悉線程,就是命令模式的運用。Runnable相當於是Thread對象的命令。

4 總結

命令對象命令模式的核心,它像是一個橋樑,連接了請求調用者和接收者,並解耦了兩者的關係。它使得系統擴展變得容易。

文中例子的github地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章