Java設計模式之命令模式

本文通過解決老王經常搞錯借書人的問題,來引出行為型模式中的命令模式。為瞭在案例之上理解的更加透徹,我們需要瞭解命令模式在源碼中的應用。最後指出命令模式的應用場景和優缺點。

讀者可以拉取完整代碼到本地進行學習,實現代碼均測試通過後上傳到碼雲。

一、引出問題

老王的書房藏書越來越多,每天來借書的人絡繹不絕。每天有人借書、還書、老王將A借的書算到B頭上的烏龍事件頻出。老王和小王就商量著手解決這個問題。

小王提議,在老王和借書者之間再增加一個“記錄員”角色,記錄員隻管報名字就行瞭,具體是借什麼書由借書者自己決定就好瞭。

老王說:這能解決部分問題。但在真實的場景下,不可能來一個借書者“記錄員”就跑一趟。而且借書者有時候會借一半臨時有事就不借瞭。這些問題你也要考慮進去。

老王接著說:你應該,在“記錄員”角色中,增加一個隊列,將所有借書者都放到一個隊列中,既有往隊列中放命令的方法,也有從命令中移除的方法,方便“記錄員”請求排隊和“撤銷”。

二、命令模式的概念和應用

老王提出來的正是命令模式的“白話文解釋”。我們來看命令模式的官方概念:將一個請求封裝為一個對象,使發出請求的責任和執行請求的責任分割開,解耦合。這樣兩者之間通過命令對象進行溝通,這樣方便將命令對象進行存儲、傳遞、調用、增加與管理。

在命令模式中有三個角色:

抽象命令類(Command)角色: 定義命令的接口,聲明執行的方法。

實現者/接收者(Receiver)(老王)角色: 接收者,真正執行命令的對象。任何類都可能成為一個接收者,隻要它能夠實現命令要求實現的相應功能。

具體命令(Concrete Command)(記錄員)角色:具體的命令,實現命令接口;通常會持有接收者,並調用接收者的功能來完成命令要執行的操作。

我們基於概念和角色劃分,實現代碼:

抽象命令類:

/**
 * 抽象命令類
 * @author tcy
 * @Date 25-08-2022
 */
public interface AbstractCommand {

    //隻需要定義一個統一的執行方法
    void execute();
}

具體命令角色(老王):

/**
 * 具體命令
 * @author tcy
 * @Date 25-08-2022
 */
public class ConcreteCommand implements AbstractCommand {

    //持有接受者對象
    private String clent;

    public ConcreteCommand(String clent){
        this.clent = clent;
    }

    @Override
    public void execute() {
        System.out.println("具體執行者角色(老王):"+clent+"借書...");

    }
}

接收者(記錄員):

/**
 * 接收者
 * @author tcy
 * @Date 25-08-2022
 */
public class ReceiverCommand {

    //可以持有很多的命令對象
    private ArrayList<AbstractCommand> commands;

    public ReceiverCommand() {
        commands = new ArrayList();
    }

    public void setCommand(AbstractCommand cmd){
        commands.add(cmd);
    }

    public void removeCommand(AbstractCommand cmd){
        commands.remove(cmd);
    }

    // 發出命令
    public void borrowBookMeaaage() {
        System.out.println("接受者角色(記錄員):有人來借書啦...");
        //通知全部命令
        for (int i = 0; i < commands.size(); i++) {
            AbstractCommand cmd = commands.get(i);
            if (cmd != null) {
                cmd.execute();
            }
        }
    }
}

客戶端(借書者):

/**
 * @author tcy
 * @Date 25-08-2022
 */
public class Client {

    public static void main(String[] args) {

        //創建接收者
        //將訂單和接收者封裝成命令對象
        ConcreteCommand cmd1 = new ConcreteCommand( "A");
        ConcreteCommand cmd2 = new ConcreteCommand( "B");

        //創建具體命令者
        ReceiverCommand invoker = new ReceiverCommand();
        invoker.setCommand(cmd1);
        invoker.setCommand(cmd2);

        //喊一聲有人要借書
        invoker.borrowBookMeaaage();


    }

}

基於命令模式實現的代碼就實現瞭,但是看懂代碼是一回事,自己能寫出來就是另外一回事瞭。讀者最好根據案例重新仿寫一遍。

三、源碼中的應用

在源碼中使用命令模式的典型案例就是Jdk多線程章節中的Runnable ,Runnable 相當於命令模式中的抽象命令角色。Runnable 中的 run() 方法就當於 execute() 方法。

我們知道,Java中一個類實現Runnable 接口,那麼該類就認為是一個線程,就相當於命令模式中的具體命令角色。

當我們調用start()方法後,就可以與別的線程強占CPU的資源,在占用CPU的線程中就會執行run()方法。CPU的調度者就相當於具體命令角色也即記錄員。Runnable 就完美的實現瞭用戶自定義線程和CPU的解耦合。

命令模式在Runnable 中的應用應該很好理解。

四、總結

優點很明顯,解耦瞭命令請求與實現,很容易的可以增加新命令,支持命令隊列。

但是,這樣會不可避免的使具體命令類過多,增加瞭理解上的困難。

設計模式學到這種程度,我們就會發現設計模式不是一種單一的技術,而是各種技術的綜合體。

我們在學習設計模式的時候一定不要僅局限於一種模式,而是站在一定的高度去整體衡量哪種設計模式才是最優的。

有時候我們會發現,使用設計模式會讓我們的代碼變得更加的復雜,但以自己目前的開發經驗又不能確定是否采用設計模式是一個好的選擇。

歸根結低,還是我們對設計模式掌握的不夠熟練,這就需要我們繼續深入學習設計模式,當我的學完再回頭看這些問題,就很自然的迎刃而解瞭。

以上就是這篇文章的全部內容瞭,希望本文的內容對大傢的學習或者工作具有一定的參考學習價值,謝謝大傢對WalkonNet的支持。如果你想瞭解更多相關內容請查看下面相關鏈接

推薦閱讀: