使用命令模式重構播放器控制條


本文節選自《設計模式就該這樣學》

1 命令模式的UML類圖

命令模式的UML類圖如下圖所示。

file

2 使用命令模式重構播放器控制條

假如我們開發一個播放器,播放器有播放功能、拖動進度條功能、停止播放功能、暫停功能,我們在操作播放器的時候並不是直接調用播放器的方法,而是通過一個控制條去傳達指令給播放器內核,具體傳達什么指令,會被封裝為一個個按鈕。那么每個按鈕就相當於對一條命令的封裝。用控制條實現了用戶發送指令與播放器內核接收指令的解耦。下面來看代碼,首先創建播放器內核GPlayer類。


public class GPlayer {

    public void play(){
        System.out.println("正常播放");
    }

    public void speed(){
        System.out.println("拖動進度條");
    }

    public void stop(){
        System.out.println("停止播放");
    }

    public void pause(){
        System.out.println("暫停播放");
    }
		
}

創建命令接口IAction類。


public interface IAction {

    void execute();
		
}

然后分別創建操作播放器可以接收的指令,播放指令PlayAction類的代碼如下。


public class PlayAction implements IAction {

    private GPlayer gplayer;

    public PlayAction(GPlayer gplayer) {
        this.gplayer = gplayer;
    }

    public void execute() {
        gplayer.play();
    }
		
}

暫停指令PauseAction類的代碼如下。


public class PauseAction implements IAction {

    private GPlayer gplayer;

    public PauseAction(GPlayer gplayer) {
        this.gplayer = gplayer;
    }

    public void execute() {
        gplayer.pause();
    }
		
}

拖動進度條指令SpeedAction類的代碼如下。


public class SpeedAction implements IAction {

    private GPlayer gplayer;

    public SpeedAction(GPlayer gplayer) {
        this.gplayer = gplayer;
    }

    public void execute() {
        gplayer.speed();
    }
		
}

停止播放指令StopAction類的代碼如下。


public class StopAction implements IAction {

    private GPlayer gplayer;

    public StopAction(GPlayer gplayer) {
        this.gplayer = gplayer;
    }

    public void execute() {
        gplayer.stop();
    }
}

最后創建控制條Controller類。


public class Controller {
    private List<IAction> actions = new ArrayList<IAction>();
    public void addAction(IAction action){
        actions.add(action);
    }

    public void execute(IAction action){
        action.execute();
    }

    public void executes(){
        for(IAction action : actions){
            action.execute();
        }
        actions.clear();
    }
}

從上面代碼來看,控制條可以執行單條命令,也可以批量執行多條命令。下面來看客戶端測試代碼。


public static void main(String[] args) {

        GPlayer player = new GPlayer();
        Controller controller = new Controller();
        controller.execute(new PlayAction(player));

        controller.addAction(new PauseAction(player));
        controller.addAction(new PlayAction(player));
        controller.addAction(new StopAction(player));
        controller.addAction(new SpeedAction(player));
        controller.executes();
}
		

由於控制條已經與播放器內核解耦了,以后如果想擴展新命令,只需增加命令即可,控制條的結構無須改動。

3 命令模式在JDK源碼中的應用

首先來看JDK中的Runnable接口,Runnable相當於命令的抽象,只要是實現了Runnable接口的類都被認為是一個線程。


public interface Runnable {
    public abstract void run();
}

實際上調用線程的start()方法之后,就有資格去搶CPU資源,而不需要編寫獲得CPU資源的邏輯。而線程搶到CPU資源后,就會執行run()方法中的內容,用Runnable接口把用戶請求和CPU執行進行解耦。

4 命令模式在JUnit源碼中的應用

再來看一個大家非常熟悉的junit.framework.Test接口。


package junit.framework;

public interface Test {
    public abstract int countTestCases();

    public abstract void run(TestResult result);
}


Test接口中有兩個方法,第一個是countTestCases()方法,用來統計當前需要執行的測試用例總數。第二個是run()方法,用來執行具體的測試邏輯,其參數TestResult是用來返回測試結果的。實際上,我們在平時編寫測試用例的時候,只需要實現Test接口就被認為是一個測試用例,那么在執行的時候就會被自動識別。通常做法都是繼承TestCase類,不妨來看一下TestCase的源碼。


public abstract class TestCase extends Assert implements Test {
        ...
        public void run(TestResult result) {
        result.run(this);
     }
     ...
}

實際上,TestCase類也實現了Test接口。我們繼承TestCase類,相當於也實現了Test接口,自然就會被掃描成為一個測試用例。
關注微信公眾號『 Tom彈架構 』回復“設計模式”可獲取完整源碼。

【推薦】Tom彈架構:30個設計模式真實案例(附源碼),挑戰年薪60W不是夢

本文為“Tom彈架構”原創,轉載請注明出處。技術在於分享,我分享我快樂!
如果本文對您有幫助,歡迎關注和點贊;如果您有任何建議也可留言評論或私信,您的支持是我堅持創作的動力。關注微信公眾號『 Tom彈架構 』可獲取更多技術干貨!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM