1. 程式人生 > >設計模式(十) — 命令模式

設計模式(十) — 命令模式

讓程式暢通執行——命令模式

命令模式的定義

將一個請求封裝成一個物件,從而讓使用者使用不同的請求把客戶端引數化;對請求排隊或者記錄請求日誌,以及支援可撤銷的操作。

命令模式的使用場景

需要抽象出待執行的動作,然後以引數的形式提供出來——類似於過程設計中的回撥機制,而命令模式真是回撥機制的一個面向物件的替代品。 在不同的時刻指定、排列和執行請求。一個命令物件可以有與初始請求無關的生存期。 需要支援取消操作。 支援修改日誌功能,這樣當系統崩潰時,這些修改可以被重做一遍。 需要支援事物操作。

命令模式的例項

角色介紹 Receiver:接收者角色 負責實施或執行一個請求,說得通俗點就是,執行具體邏輯的角色。 Command:命令角色

定義所有具體命令類的抽象介面。 ConcreteCommand:具體命令角色 實現Command介面,在實現方法中呼叫接受者角色的相關方法,在接收者和命令執行的具體行為之間加以弱耦合。 Invoker:請求者角色 呼叫命令物件執行具體的請求。

以俄羅斯方塊為例,俄羅斯方塊有四個按鈕,左右移動、快速落下和變換形狀。四個按鈕相當於請求者,執行具體按鈕命令的邏輯方法可以看作是命令角色。

接收者角色 俄羅斯方塊遊戲 TetrisMachine.class

public class TetrisMachine {
    private static final String TAG = "TetrisMachine"
; /** * description: 真正處理"向左"操作的邏輯程式碼 */ public void toLeft() { Log.i(TAG, "向左"); } /** * description: 真正處理"向右"操作的邏輯程式碼 */ public void toRight() { Log.i(TAG, "向右"); } /** * description: 真正處理"快速下落"操作的邏輯程式碼 */ public void fastToBottom
() { Log.i(TAG, "快速下落"); } /** * description: 真正處理"改變形狀"操作的邏輯程式碼 */ public void transform() { Log.i(TAG, "改變形狀"); } }

命令者抽象 Command.class

public interface Command {
    /**
     * description: 命令執行方法
     */
    void execute();
}

具體命令者 向右 RightCommand.class

public class RightCommand implements Command {
    //持有一個接收者俄羅斯方塊遊戲物件的引用
    private TetrisMachine tetrisMachine;

    public RightCommand(TetrisMachine tetrisMachine) {
        this.tetrisMachine = tetrisMachine;
    }

    @Override
    public void execute() {
        //呼叫遊戲機裡的具體方法執行操作
        tetrisMachine.toRight();
    }
}

具體命令者 向左 LeftCommand.class

public class LeftCommand implements Command {
    //持有一個接收者俄羅斯方塊遊戲物件的引用
    private TetrisMachine tetrisMachine;

    public LeftCommand(TetrisMachine tetrisMachine) {
        this.tetrisMachine = tetrisMachine;
    }

    @Override
    public void execute() {
        //呼叫遊戲機裡的具體方法執行操作
        tetrisMachine.toLeft();
    }
}

具體命令者 快速落下 FallCommand.class

public class FallCommand implements Command {
    //持有一個接收者俄羅斯方塊遊戲物件的引用
    private TetrisMachine tetrisMachine;

    public FallCommand(TetrisMachine tetrisMachine) {
        this.tetrisMachine = tetrisMachine;
    }

    @Override
    public void execute() {
        //呼叫遊戲機裡的具體方法執行操作
        tetrisMachine.fastToBottom();
    }
}

具體命令者 改變形狀 TransformCommand.class

public class TransformCommand implements Command {
    //持有一個接收者俄羅斯方塊遊戲物件的引用
    private TetrisMachine tetrisMachine;

    public TransformCommand(TetrisMachine tetrisMachine) {
        this.tetrisMachine = tetrisMachine;
    }

    @Override
    public void execute() {
        //呼叫遊戲機裡的具體方法執行操作
        tetrisMachine.transform();
    }
}

請求者 Buttons.class

public class Buttons {
    //向左移動的命令物件引用
    private LeftCommand leftCommand;
    //向右移動的命令物件引用
    private RightCommand rightCommand;
    //快速落下的命令物件引用
    private FallCommand fallCommand;
    //變換形狀的命令物件引用
    private TransformCommand transformCommand;

    /**
     * description: 設定向左移動的命令物件
     */
    public void setLeftCommand(LeftCommand leftCommand) {
        this.leftCommand = leftCommand;
    }

    /**
     * description: 設定向右移動的命令物件
     */
    public void setRightCommand(RightCommand rightCommand) {
        this.rightCommand = rightCommand;
    }

    /**
     * description: 設定快速落下的命令物件
     */
    public void setFallCommand(FallCommand fallCommand) {
        this.fallCommand = fallCommand;
    }

    /**
     * description: 設定變換形狀的命令物件
     */
    public void setTransformCommand(TransformCommand transformCommand) {
        this.transformCommand = transformCommand;
    }

    /**
     * description: 按下按鈕向左移動
     */
    public void toLeft() {
        leftCommand.execute();
    }

    /**
     * description: 按下按鈕向右移動
     */
    public void toRight() {
        rightCommand.execute();
    }

    /**
     * description: 按下按鈕快速下落
     */
    public void fall() {
        fallCommand.execute();
    }

    /**
     * description: 按下按鈕改變形狀
     */
    public void transform() {
        transformCommand.execute();
    }
}

客戶端 Player.class

public class Player {
    public void play() {
        //首先要有俄羅斯方塊遊戲
        TetrisMachine tetrisMachine = new TetrisMachine();

        //根據遊戲我們構成4種命令
        LeftCommand leftCommand = new LeftCommand(tetrisMachine);
        RightCommand rightCommand = new RightCommand(tetrisMachine);
        FallCommand fallCommand = new FallCommand(tetrisMachine);
        TransformCommand transformCommand = new TransformCommand(tetrisMachine);

        //按鈕可以執行不同的指令
        Buttons buttons = new Buttons();
        buttons.setLeftCommand(leftCommand);
        buttons.setRightCommand(rightCommand);
        buttons.setFallCommand(fallCommand);
        buttons.setTransformCommand(transformCommand);

        //具體按下哪個按鈕玩家說了算
        buttons.toLeft();
        buttons.toRight();
        buttons.fall();
        buttons.transform();
    }
}

看到這,可能會犯嘀咕,明明可以很簡單的呼叫,像這樣:

        TetrisMachine tetrisMachine = new TetrisMachine();
        tetrisMachine.toLeft();
        tetrisMachine.toRight();
        tetrisMachine.fastToBottom();
        tetrisMachine.transform();

而且要是增加或者修改一個命令,需要改4處地方,這哪裡是設計模式呀。

這個確實可以兩個類搞定,但是命令模式的主旨是對命令請求者和命令接收者的解耦,方便對命令進行各種控制。什麼叫方便對命令進行控制呢,打個比方,我們需要在按下俄羅斯方塊遊戲的時候記錄命令執行的日誌、命令的執行有某些條件。如果直接用兩個類就會有大量的業務邏輯要在客戶端進行處理,當命令增加,對每個命令的控制增加時,就會在Client裡面產生大量的變化點,這樣耦合就出來了。

小結

優點

更弱的耦合性、更靈活的控制性以及更好的擴充套件性。

缺點

設計模式的通病,類的膨脹,大量衍生類的建立。