【行為型模式十七】命令模式-2(Command)

3.5 隊列請求##

所謂隊列請求涵紊,就是對命令對象進行排隊傍妒,組成工作隊列,然后依次取出命令對象來執(zhí)行栖袋。多用多線程或者線程池來進行命令隊列的處理拍顷,當(dāng)然也可以不用多線程,就是一個線程塘幅,一個命令一個命令的循環(huán)處理昔案,就是慢點。

繼續(xù)宏命令的例子电媳,其實在后廚踏揣,會收到很多很多的菜單,一般是按照菜單傳遞到后廚的先后順序來進行處理匾乓,對每張菜單捞稿,假定也是按照菜品的先后順序進行制作,那么在后廚就自然形成了一個菜品的隊列拼缝,也就是很多個用戶的命令對象的隊列娱局。

后廚有很多廚師郎笆,每個廚師都從這個命令隊列里面取出一個命令红氯,然后按照命令做出菜來这难,就相當(dāng)于多個線程在同時處理一個隊列請求行嗤。

因此后廚就是一個很典型的隊列請求的例子。

提示一點:后廚的廚師與命令隊列之間是沒有任何關(guān)聯(lián)的匀泊,也就是說是完全解耦的烘绽。命令隊列是客戶發(fā)出的命令敌卓,廚師只是負責(zé)從隊列里面取出一個,處理抹缕,然后再取下一個澈蟆,再處理,僅此而已卓研,廚師不知道也不管客戶是誰趴俘。

  1. 如何實現(xiàn)命令模式的隊列請求

(1)先從命令接口開始,除了execute方法外鉴分,新加了一個返回發(fā)出命令的桌號哮幢,就是點菜的桌號,還有一個是為命令對象設(shè)置接收者的方法志珍,也把它添加到接口上,這個是為了后面多線程處理的時候方便使用垛叨。示例代碼如下:

/** 
 * 命令接口伦糯,聲明執(zhí)行的操作 
 */  
public interface Command {  
    /** 
     * 執(zhí)行命令對應(yīng)的操作 
     */  
    public void execute();  
    /** 
     * 設(shè)置命令的接收者 
     * @param cookApi 命令的接收者  
     */  
    public void setCookApi(CookApi cookApi);  
    /** 
     * 返回發(fā)起請求的桌號,就是點菜的桌號 
     * @return 發(fā)起請求的桌號 
     */  
    public int getTableNum();  
}

(2)廚師的接口也發(fā)生了一點變化嗽元,在cook的方法上添加了發(fā)出命令的桌號敛纲,這樣在多線程輸出信息的時候,才知道到底是在給哪個桌做菜剂癌,示例代碼如下:

/** 
 * 廚師的接口 
 */  
public interface CookApi {  
    /** 
     * 示意淤翔,做菜的方法 
     * @param tableNum 點菜的桌號 
     * @param name 菜名 
     */  
    public void cook(int tableNum,String name);  
}  

** (3)開始來實現(xiàn)命令接口,為了簡單佩谷,這次只有熱菜旁壮,因為要做工作都在后廚的命令隊列里面,因此涼菜就不要了谐檀,示例代碼如下:**

/** 
 * 命令對象抡谐,綠豆排骨煲 
 */  
public class ChopCommand implements Command{  
    /** 
     * 持有具體做菜的廚師的對象 
     */  
    private CookApi cookApi = null;  
    /** 
     * 設(shè)置具體做菜的廚師的對象 
     * @param cookApi 具體做菜的廚師的對象 
     */  
    public void setCookApi(CookApi cookApi) {  
        this.cookApi = cookApi;  
    }  
    /** 
     * 點菜的桌號 
     */  
    private int tableNum;  
    /** 
     * 構(gòu)造方法,傳入點菜的桌號 
     * @param tableNum 點菜的桌號 
     */  
    public ChopCommand(int tableNum){  
        this.tableNum = tableNum;  
    }  
    public int getTableNum(){  
        return this.tableNum;  
    }  
    public void execute() {  
        this.cookApi.cook(tableNum,"綠豆排骨煲");  
    }  
}  

(4)接下來構(gòu)建很重要的命令對象的隊列桐猬,其實也不是有多難麦撵,多個命令對象嘛,用個集合來存儲就好了溃肪,然后按照放入的順序免胃,先進先出即可。

/** 
 * 命令隊列類 
 */  
public class CommandQueue {  
    /** 
     * 用來存儲命令對象的隊列 
     */  
    private static List<Command> cmds = new ArrayList<Command>();  
    /** 
     * 服務(wù)員傳過來一個新的菜單惫撰,需要同步羔沙, 
     * 因為同時會有很多的服務(wù)員傳入菜單,而同時又有很多廚師在從隊列里取值 
     * @param menu 傳入的菜單 
     */  
    public synchronized static void addMenu(MenuCommand menu){  
        //一個菜單對象包含很多命令對象  
        for(Command cmd : menu.getCommands()){  
            cmds.add(cmd);  
        }  
    }  
    /** 
     * 廚師從命令隊列里面獲取命令對象進行處理润绎,也是需要同步的 
     */  
    public synchronized static Command getOneCommand(){  
        Command cmd = null;  
        if(cmds.size() > 0 ){  
            //取出隊列的第一個撬碟,因為是約定的按照加入的先后來處理  
            cmd = cmds.get(0);  
            //同時從隊列里面取掉這個命令對象  
            cmds.remove(0);  
        }  
        return cmd;  
    }  
}  

提示:這里并沒有考慮一些復(fù)雜的情況诞挨,比如:如果命令隊列里面沒有命令,而廚師又來獲取命令怎么辦呢蛤?這里只是做一個基本的示范惶傻,并不是完整的實現(xiàn),所以這里就沒有去處理這些問題了其障,當(dāng)然出現(xiàn)這種問題银室,就需要使用wait/notify來進行線程調(diào)度了

(5)有了命令隊列励翼,誰來向這個隊列里面?zhèn)魅朊钅兀?/strong>

很明顯是服務(wù)員蜈敢,當(dāng)客戶點菜完成,服務(wù)員就會執(zhí)行菜單汽抚,現(xiàn)在執(zhí)行菜單就相當(dāng)于把菜單直接傳遞給后廚抓狭,也就是要把菜單里的所有命令對象加入到命令隊列里面。因此菜單對象的實現(xiàn)需要改變造烁,示例代碼如下:

/** 
 * 菜單對象否过,是個宏命令對象 
 */  
public class MenuCommand implements Command {  
    /** 
     * 用來記錄組合本菜單的多道菜品,也就是多個命令對象 
     */  
    private Collection<Command> col = new ArrayList<Command>();  
    /** 
     * 點菜惭蟋,把菜品加入到菜單中 
     * @param cmd 客戶點的菜 
     */  
    public void addCommand(Command cmd){  
        col.add(cmd);  
    }  
    public void setCookApi(CookApi cookApi){  
        //什么都不用做  
    }  
    public int getTableNum(){  
        //什么都不用做  
        return 0;  
    }  
    /** 
     * 獲取菜單中的多個命令對象 
     * @return 菜單中的多個命令對象 
     */  
    public Collection<Command> getCommands(){  
        return this.col;  
    }     
 
    public void execute() {  
        //執(zhí)行菜單就是把菜單傳遞給后廚  
        CommandQueue.addMenu(this);  
    }  
}  

(6)現(xiàn)在有了命令隊列苗桂,也有人負責(zé)向隊列里面添加命令了,可是誰來執(zhí)行命令隊列里面的命令呢告组?

答案是:由廚師從命令隊列里面獲取命令煤伟,并真正處理命令,而且廚師在處理命令前會把自己設(shè)置到命令對象里面去當(dāng)接收者木缝,表示這個菜由我來實際做便锨。

廚師對象的實現(xiàn),大致有如下的改變:

為了更好的體現(xiàn)命令隊列的用法氨肌,再說實際情況也是多個廚師鸿秆,這里用多線程來模擬多個廚師,他們自己從命令隊列里面獲取命令怎囚,然后處理命令卿叽,然后再獲取下一個,如此反復(fù)恳守,因此廚師類要實現(xiàn)多線程接口考婴。

還有一個改變,為了在多線程中輸出信息催烘,讓我們知道是哪一個廚師在執(zhí)行命令沥阱,給廚師添加了一個姓名的屬性,通過構(gòu)造方法傳入伊群。

另外一個改變是為了在多線程中看出效果考杉,在廚師真正做菜的方法里面使用隨機數(shù)模擬了一個做菜的時間策精。

好了,介紹完了改變的地方崇棠,一起看看代碼吧咽袜,示例代碼如下:

/** 
 * 廚師對象,做熱菜的廚師 
 */  
public class HotCook implements CookApi,Runnable{  
    /** 
     * 廚師姓名 
     */  
    private String name;  
    /** 
     * 構(gòu)造方法枕稀,傳入廚師姓名 
     * @param name 廚師姓名 
     */  
    public HotCook(String name){  
        this.name = name;  
    }     
    public void cook(int tableNum,String name) {  
        //每次做菜的時間是不一定的询刹,用個隨機數(shù)來模擬一下  
        int cookTime = (int)(20 * Math.random());  
        System.out.println(this.name+"廚師正在為"+tableNum+"號桌做:"+name);  
        try {  
            //讓線程休息這么長時間,表示正在做菜  
            Thread.sleep(cookTime);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
        System.out.println(this.name+"廚師為"+tableNum+"號桌做好了:"+name+",共計耗時="+cookTime+"秒");  
    }  
    public void run() {  
        while(true){  
            //到命令隊列里面獲取命令對象  
            Command cmd = CommandQueue.getOneCommand();  
            if(cmd != null){  
                //說明取到命令對象了萎坷,這個命令對象還沒有設(shè)置接收者  
                //因為前面都還不知道到底哪一個廚師來真正執(zhí)行這個命令  
                //現(xiàn)在知道了凹联,就是當(dāng)前廚師實例,設(shè)置到命令對象里面  
                cmd.setCookApi(this);  
                //然后真正執(zhí)行這個命令  
                cmd.execute();  
            }  
            //休息1秒  
            try {  
                Thread.sleep(1000L);  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
        }  
    }  
} 

(7)該來看看服務(wù)員類了哆档,由于現(xiàn)在考慮了后廚的管理蔽挠,因此從實際來看,這次服務(wù)員也不知道到底命令的真正接收者是誰了瓜浸,也就是說服務(wù)員也不知道某個菜到底最后由哪一位廚師完成象泵,所以服務(wù)員類就簡單了。

組裝命令對象和接收者的功能后移到廚師類的線程里面了斟叼,當(dāng)某個廚師從命令隊列里面獲取一個命令對象的時候,這個廚師就是這個命令的真正接收者春寿。

/** 
 * 服務(wù)員朗涩,負責(zé)組合菜單,還負責(zé)執(zhí)行調(diào)用 
 */  
public class Waiter {  
    /** 
     * 持有一個宏命令對象——菜單 
     */  
    private MenuCommand menuCommand = new MenuCommand();  
    /** 
     * 客戶點菜 
     * @param cmd 客戶點的菜,每道菜是一個命令對象 
     */  
    public void orderDish(Command cmd){  
        //添加到菜單中  
        menuCommand.addCommand(cmd);  
    }  
    /** 
     * 客戶點菜完畢绑改,表示要執(zhí)行命令了谢床,這里就是執(zhí)行菜單這個組合命令 
     */  
    public void orderOver(){  
        this.menuCommand.execute();  
    }  
}  

(8)在見到曙光之前,還有一個問題要解決厘线,就是誰來啟動多線程的廚師呢识腿?

為了實現(xiàn)后廚的管理,為此專門定義一個后廚管理的類造壮,在這個類里面去啟動多個廚師的線程渡讼。而且這種啟動在運行期間應(yīng)該只有一次。示例代碼如下:

/** 
 * 后廚的管理類耳璧,通過此類讓后廚的廚師進行運行狀態(tài) 
 */  
public class CookManager {  
    /** 
     * 用來控制是否需要創(chuàng)建廚師成箫,如果已經(jīng)創(chuàng)建過了就不要再執(zhí)行了 
     */  
    private static boolean runFlag = false;  
    /** 
     * 運行廚師管理,創(chuàng)建廚師對象并啟動他們相應(yīng)的線程旨枯, 
     * 無論運行多少次蹬昌,創(chuàng)建廚師對象和啟動線程的工作就只做一次 
     */  
    public static void runCookManager(){  
        if(!runFlag){  
            runFlag = true;  
            //創(chuàng)建三位廚師  
            HotCook cook1 = new HotCook("張三");  
            HotCook cook2 = new HotCook("李四");  
            HotCook cook3 = new HotCook("王五");  
 
            //啟動他們的線程  
            Thread t1 = new Thread(cook1);  
            t1.start();  
            Thread t2 = new Thread(cook2);  
            t2.start();  
            Thread t3 = new Thread(cook3);  
            t3.start();  
        }  
    }  
}  

(9)曙光來臨了,寫個客戶端測試測試攀隔,示例代碼如下:

public class Client {  
    public static void main(String[] args) {  
        //先要啟動后臺皂贩,讓整個程序運行起來  
        CookManager.runCookManager();  
          
        //為了簡單栖榨,直接用循環(huán)模擬多個桌號點菜  
        for(int i = 0;i<5;i++){  
            //創(chuàng)建服務(wù)員  
            Waiter waiter = new Waiter();  
            //創(chuàng)建命令對象,就是要點的菜  
            Command chop = new ChopCommand(i);  
            Command duck = new DuckCommand(i);  
 
            //點菜明刷,就是把這些菜讓服務(wù)員記錄下來  
            waiter.orderDish(chop);  
            waiter.orderDish(duck);  
 
            //點菜完畢  
            waiter.orderOver();  
        }         
    }  
} 

(10)運行一下婴栽,看看效果,可能每次運行的效果不一樣遮精,畢竟是使用多線程在處理請求隊列居夹,某次運行的結(jié)果如下:

Paste_Image.png

好好觀察上面的數(shù)據(jù),在多線程環(huán)境下本冲,雖然保障了命令對象取出的順序是先進先出准脂,但是究竟是哪一個廚師來做,還有具體做多長時間都是不定的檬洞。

3.7 命令模式的優(yōu)缺點##

  1. 更松散的耦合

命令模式使得發(fā)起命令的對象——客戶端狸膏,和具體實現(xiàn)命令的對象——接收者對象完全解耦,也就是說發(fā)起命令的對象添怔,完全不知道具體實現(xiàn)對象是誰湾戳,也不知道如何實現(xiàn)。

  1. 更動態(tài)的控制

命令模式把請求封裝起來广料,可以動態(tài)對它進行參數(shù)化砾脑、隊列化和日志化等操作,從而使得系統(tǒng)更靈活艾杏。

  1. 能很自然的復(fù)合命令

命令模式中的命令對象韧衣,能夠很容易的組合成為復(fù)合命令,就是前面講的宏命令购桑,從而使系統(tǒng)操作更簡單畅铭,功能更強大。

  1. 更好的擴展性

由于發(fā)起命令的對象和具體的實現(xiàn)完全解耦勃蜘,因此擴展新的命令就很容易硕噩,只需要實現(xiàn)新的命令對象,然后在裝配的時候缭贡,把具體的實現(xiàn)對象設(shè)置到命令對象里面炉擅,然后就可以使用這個命令對象,已有的實現(xiàn)完全不用變化匀归。

3.8 思考命令模式##

  1. 命令模式的本質(zhì)

命令模式的本質(zhì):封裝請求坑资。

前面講了,命令模式的關(guān)鍵就是把請求封裝成為命令對象穆端,然后就可以對這個對象進行一系列的處理了袱贮,比如上面講到的參數(shù)化配置、可撤銷操作、宏命令攒巍、隊列請求嗽仪、日志請求等功能處理。

  1. 何時選用命令模式

建議在如下情況中柒莉,選用命令模式:

如果需要抽象出需要執(zhí)行的動作闻坚,并參數(shù)化這些對象,可以選用命令模式兢孝,把這些需要執(zhí)行的動作抽象成為命令窿凤,然后實現(xiàn)命令的參數(shù)化配置

如果需要在不同的時刻指定、排列和執(zhí)行請求跨蟹,可以選用命令模式雳殊,把這些請求封裝成為命令對象,然后實現(xiàn)把請求隊列化

如果需要支持取消操作窗轩,可以選用命令模式夯秃,通過管理命令對象,能很容易的實現(xiàn)命令的恢復(fù)和重做的功能

如果需要支持當(dāng)系統(tǒng)崩潰時痢艺,能把對系統(tǒng)的操作功能重新執(zhí)行一遍仓洼,可以選用命令模式,把這些操作功能的請求封裝成命令對象堤舒,然后實現(xiàn)日志命令色建,就可以在系統(tǒng)恢復(fù)回來后,通過日志獲取命令列表舌缤,從而重新執(zhí)行一遍功能

在需要事務(wù)的系統(tǒng)中镀岛,可以選用命令模式,命令模式提供了對事務(wù)進行建模的方法友驮,命令模式有一個別名就是Transaction。

3.9 退化的命令模式##

在領(lǐng)會了命令模式本質(zhì)后驾锰,來思考一個命令模式退化的情況卸留。

前面講到了智能命令,如果命令的實現(xiàn)對象超級智能椭豫,實現(xiàn)了命令所要求的功能耻瑟,那么就不需要接收者了,既然沒有了接收者赏酥,那么也就不需要組裝者了喳整。

  1. 舉個最簡單的示例來說明

比如現(xiàn)在要實現(xiàn)一個打印服務(wù),由于非常簡單裸扶,所以基本上就沒有什么講述框都,依次來看,命令接口定義如下:

public interface Command {  
    public void execute();  
}  

命令的實現(xiàn)示例代碼如下:

public class PrintService implements Command{  
    /** 
     * 要輸出的內(nèi)容 
     */  
    private String str = "";  
    /** 
     * 構(gòu)造方法呵晨,傳入要輸出的內(nèi)容 
     * @param s 要輸出的內(nèi)容 
     */  
    public PrintService(String s){  
        str = s;  
    }  
    public void execute() {  
        //智能的體現(xiàn)魏保,自己知道怎么實現(xiàn)命令所要求的功能熬尺,并真的實現(xiàn)了相應(yīng)的功能,不再轉(zhuǎn)調(diào)接收者了  
        System.out.println("打印的內(nèi)容為="+str);  
    }  
}  

此時的Invoker示例代碼如下:

public class Invoker {  
    /** 
     * 持有命令對象 
     */  
    private Command cmd = null;  
    /** 
     * 設(shè)置命令對象 
     * @param cmd 命令對象 
     */  
    public void setCmd(Command cmd){  
        this.cmd = cmd;  
    }  
    /** 
     * 開始打印 
     */  
    public void startPrint(){  
        //執(zhí)行命令的功能  
        this.cmd.execute();  
    }  
}  

最后看看客戶端的代碼谓罗,示例如下:

public class Client {  
    public static void main(String[] args) {  
        //準備要發(fā)出的命令  
        Command cmd = new PrintService("退化的命令模式示例");  
        //設(shè)置命令給持有者  
        Invoker invoker = new Invoker();  
        invoker.setCmd(cmd);      
 
        //按下按鈕粱哼,真正啟動執(zhí)行命令  
        invoker.startPrint();  
    }  
}  

測試結(jié)果如下:

打印的內(nèi)容為=退化的命令模式示例
  1. 繼續(xù)變化

如果此時繼續(xù)變化,Invoker也開始變得智能化檩咱,在Invoker的startPrint方法里面揭措,Invoker加入了一些實現(xiàn),同時Invoker對持有命令也有意見刻蚯,覺得自己是個傀儡绊含,要求改變一下,直接在調(diào)用方法的時候傳遞命令對象進來芦倒,示例代碼如下:

public class Invoker {  
    public void startPrint(Command cmd){      
        System.out.println("在Invoker中艺挪,輸出服務(wù)前");  
        cmd.execute();  
        System.out.println("輸出服務(wù)結(jié)束");  
    }  
}  

看起來Invoker退化成一個方法了。

這個時候Invoker很高興兵扬,宣稱自己是一個智能的服務(wù)麻裳,不再是一個傻傻的轉(zhuǎn)調(diào)者,而是有自己功能的服務(wù)了器钟。這個時候Invoker調(diào)用命令對象的執(zhí)行方法津坑,也不叫轉(zhuǎn)調(diào),改名叫“回調(diào)”傲霸,意思是在我Invoker需要的時候疆瑰,會回調(diào)你命令對象,命令對象你就乖乖的寫好實現(xiàn)昙啄,等我“回調(diào)”你就可以了穆役。

事實上這個時候的命令模式的實現(xiàn),基本上就等同于Java回調(diào)機制的實現(xiàn)梳凛,可能有些朋友看起來感覺還不是佷像耿币,那是因為在Java回調(diào)機制的常見實現(xiàn)上,經(jīng)常沒有單獨的接口實現(xiàn)類韧拒,而是采用匿名內(nèi)部類的方式來實現(xiàn)的淹接。

  1. 再進一步

把單獨實現(xiàn)命令接口的類改成用匿名內(nèi)部類實現(xiàn),這個時候就只剩下命令的接口叛溢、Invoker類塑悼,還有客戶端了

為了使用匿名內(nèi)部類楷掉,還要設(shè)置要輸出的值厢蒜,對命令接口做點小改動,增加一個設(shè)置輸出值的方法,示例代碼如下:

public interface Command {  
    public void execute();  
    /** 
     * 設(shè)置要輸出的內(nèi)容 
     * @param s 要輸出的內(nèi)容 
     */  
    public void setStr(String s);  
}  

此時Invoker就是上面那個郭怪,而客戶端會有些改變支示,客戶端的示例代碼如下:

public class Client {  
    public static void main(String[] args) {  
        //準備要發(fā)出的命令,沒有具體實現(xiàn)類了  
        //匿名內(nèi)部類來實現(xiàn)命令   
        Command cmd = new Command() {  
            private String str = "";  
            public void setStr(String s) {  
                str = s;  
            }  
            public void execute() {  
                System.out.println("打印的內(nèi)容為="+str);  
            }  
        };
        cmd.setStr("退化的命令模式類似于Java回調(diào)的示例");        
        //這個時候的Invoker或許該稱為服務(wù)了  
        Invoker invoker = new Invoker();  
        //按下按鈕鄙才,真正啟動執(zhí)行命令  
        invoker.startPrint(cmd);  
    }  
}  

運行測試一下颂鸿,結(jié)果如下:

在Invoker中,輸出服務(wù)前  
打印的內(nèi)容為=退化的命令模式類似于Java回調(diào)的示例  
輸出服務(wù)結(jié)束  
  1. 現(xiàn)在是不是看出來了攒庵,這個時候的命令模式的實現(xiàn)嘴纺,基本上就等同于Java回調(diào)機制的實現(xiàn)。這也是很多人大談特談命令模式可以實現(xiàn)Java回調(diào)的意思浓冒。

當(dāng)然更狠的是連Invoker也不要了栽渴,直接把那個方法搬到Client中,那樣測試起來就更方便了稳懒。在實際開發(fā)中闲擦,應(yīng)用命令模式來實現(xiàn)回調(diào)機制的時候,Invoker通常還是有的场梆,但可以智能化實現(xiàn)墅冷,更準確的說Invoker充當(dāng)客戶調(diào)用的服務(wù)實現(xiàn),而回調(diào)的方法只是實現(xiàn)服務(wù)功能中的一個或者幾個步驟或油。

3.10 相關(guān)模式##

  • 命令模式和組合模式

這兩個模式可以組合使用寞忿。

在命令模式中,實現(xiàn)宏命令的功能顶岸,就可以使用組合模式來實現(xiàn)腔彰。前面的示例并沒有按照組合模式來做,那是為了保持示例的簡單辖佣,還有突出命令模式的實現(xiàn)霹抛,這點請注意。

  • 命令模式和備忘錄模式

這兩個模式可以組合使用卷谈。

在命令模式中上炎,實現(xiàn)可撤銷操作功能時,前面講了有兩種實現(xiàn)方式雏搂,其中有一種就是保存命令執(zhí)行前的狀態(tài),撤銷的時候就把狀態(tài)恢復(fù)回去寇损。如果采用這種方式實現(xiàn)凸郑,就可以考慮使用備忘錄模式。

如果狀態(tài)存儲在命令對象里面矛市,那么還可以使用原型模式芙沥,把命令對象當(dāng)作原型來克隆一個新的對象,然后把克隆出來的對象通過備忘錄模式存放。

  • 命令模式和模板方法模式

這兩個模式從某種意義上有相似的功能而昨,命令模式可以作為模板方法的一種替代模式救氯,也就是說命令模式可以模仿實現(xiàn)模板方法模式的功能

如同前面講述的退化的命令模式可以實現(xiàn)Java的回調(diào)歌憨,而Invoker智能化后向服務(wù)進化着憨,如果Invoker的方法就是一個算法骨架,其中有兩步在這個骨架里面沒有具體實現(xiàn)务嫡,需要外部來實現(xiàn)甲抖,這個時候就可以通過回調(diào)命令接口來實現(xiàn)。

而類似的功能在模板方法里面心铃,一個算法骨架准谚,其中有兩步在這個骨架里面沒有具體實現(xiàn),是先調(diào)用抽象方法去扣,然后等待子類來實現(xiàn)柱衔。

可以看出雖然實現(xiàn)方式不一樣,但是可以實現(xiàn)相同的功能愉棱。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末唆铐,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子羽氮,更是在濱河造成了極大的恐慌或链,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件档押,死亡現(xiàn)場離奇詭異澳盐,居然都是意外死亡,警方通過查閱死者的電腦和手機令宿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門叼耙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人粒没,你說我怎么就攤上這事筛婉。” “怎么了癞松?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵爽撒,是天一觀的道長。 經(jīng)常有香客問我响蓉,道長硕勿,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任枫甲,我火速辦了婚禮源武,結(jié)果婚禮上扼褪,老公的妹妹穿的比我還像新娘。我一直安慰自己粱栖,他們只是感情好话浇,可當(dāng)我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著闹究,像睡著了一般幔崖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上跋核,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天岖瑰,我揣著相機與錄音,去河邊找鬼砂代。 笑死蹋订,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的刻伊。 我是一名探鬼主播露戒,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼捶箱!你這毒婦竟也來了智什?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤丁屎,失蹤者是張志新(化名)和其女友劉穎荠锭,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體晨川,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡证九,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了共虑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片愧怜。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖妈拌,靈堂內(nèi)的尸體忽然破棺而出拥坛,到底是詐尸還是另有隱情,我是刑警寧澤尘分,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布猜惋,位于F島的核電站,受9級特大地震影響培愁,放射性物質(zhì)發(fā)生泄漏著摔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一竭钝、第九天 我趴在偏房一處隱蔽的房頂上張望梨撞。 院中可真熱鬧,春花似錦香罐、人聲如沸卧波。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽港粱。三九已至,卻和暖如春旦签,著一層夾襖步出監(jiān)牢的瞬間查坪,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工宁炫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留偿曙,地道東北人。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓羔巢,卻偏偏與公主長得像望忆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子竿秆,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 【學(xué)習(xí)難度:★★★☆☆启摄,使用頻率:★★★★☆】直接出處:命令模式梳理和學(xué)習(xí):https://github.com/...
    BruceOuyang閱讀 835評論 0 3
  • 1.初識命令模式 將一個請求封裝為一個對象,從而使你可用不同的請求對客戶進行參數(shù)化幽钢;對請求排隊或記錄請求日志歉备,以及...
    王偵閱讀 706評論 0 2
  • 1 意圖 將一個請求封裝為一個對象,從而使你可用不同的請求對客戶進行參數(shù)化匪燕;對請求排隊或記錄請求日志蕾羊,以及支持可撤...
    10xjzheng閱讀 592評論 0 1
  • javascript設(shè)計模式與開發(fā)實踐 設(shè)計模式 每個設(shè)計模式我們需要從三點問題入手: 定義 作用 用法與實現(xiàn) 單...
    穿牛仔褲的蚊子閱讀 4,057評論 0 13
  • 命令模式(行為型) 一、相關(guān)概述 1). 案例引入 Sunny軟件公司開發(fā)人員為公司內(nèi)部OA系統(tǒng)開發(fā)了一個桌面版應(yīng)...
    哈哈大圣閱讀 277評論 0 0