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

Tom彈架構發表於2021-11-22

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

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彈架構 』可獲取更多技術乾貨!

相關文章