編程模式·命令模式

將函數(shù)調用封裝成命令對象,從而支持各種后續(xù)管理:傳遞,保存,延后處理,撤銷重做等


經(jīng)典命令模式解析

命令模式的5個角色:

  1. Command:命令接口,定義execute/undo之類的接口方法。
  2. ConcreteCommand:具體的命令,實現(xiàn)命令接口。調用Receiver執(zhí)行具體命令,也可以直接執(zhí)行命令。
  3. Receiver:接收者,命令的實際執(zhí)行者。
  4. Invoker:請求者,持有命令對象,要求命令對象執(zhí)行請求。
  5. Client:創(chuàng)建命令對象,設置命令對象的接受者,把命令對象添加給請求者。驅動Invoker執(zhí)行請求。
命令模式的UML圖

偽代碼例子:

// 命令接口
interface Command{
    execute();
}
// 具體action命令
class ConcreteCommandAction : Command{
   receiver = null; 
   _construct(Receiver receiver){
        this.receiver = receiver;
   }
   execute(){
        this.receiver->action();
   }
}
// 接收者
class Reveiver{
    action(){
        // do some action
    }
}
// 請求者
class Invoker{
    command = null;
    setCommand(Command command){
        this.command = command;    
    }
    runCommand(){
       this.command->execute(); 
    }
}
// 客戶端組裝調用
client(){
    receiver = getRecevier();
    command = new ConcreteCommandAction(receiver);
    invoker = getInvoker();
    invoker->seCommand(command);
    invoker->runCommand();
}

上面一堆代碼實現(xiàn)了:請求receiver執(zhí)行action。等效代碼如下:

client(){
    recevier = getReceiver();
    receiver->action();
}

這。。。


對命令模式的簡單分析

封裝調用,完成解耦

ConcreteCommandAction封裝了action的調用,invoker與recevier完全解耦了。
封裝調用,這個大家都喜歡用。一般用到第三方庫時,會針對性的做一層封裝,統(tǒng)一邏輯,簡化接口,屏蔽細節(jié)。這在多平臺適配時幾乎是必須的。

增加命令很方便

如果我們要給Receiver增加新的行為。

  1. 簡單實現(xiàn):修改Receiver的定義,增加新的方法。(違反關閉原則,Reveiver會越來越大)
  2. 命令模式:新加命令,在execute里實現(xiàn)新的行為。(當然新的行為要能使用Receiver的公開接口組合實現(xiàn))

如:加個新行為,按需執(zhí)行action多次,可以定義個新的Command

class ConcreteCommandRepeatAction : Command{
   receiver = null; 
   times = 0;// 執(zhí)行次數(shù)
   _construct(Receiver receiver, int times){
        this.receiver = receiver;
        this.times = times;
   }
   execute(){
        while(times > 0){
            this.receiver->action();
            this.times -= 1;
        }
   }
}

這個很有誘惑力,相當于不改類定義,而給類增加新的方法。
一個極端的例子,Recevier就是個數(shù)據(jù)結構,每個具體命令操作Recevier的數(shù)據(jù)。
相當于把數(shù)據(jù)和操作分離,然后按需組合成為想要的對象。(好像面向對象就是為了把數(shù)據(jù)和操作組合在一起的)

函數(shù)調用封裝成命令對象

簡單的調用被封裝成命令對象后,有了自己的生命周期。這個有需要好處。

  1. 可以暫存下來,延后調用。
    像網(wǎng)絡請求這類情景,經(jīng)常請求轉換成命令對象,在單獨的線程里排隊執(zhí)行。
  2. 可以序列化成數(shù)據(jù),進行網(wǎng)絡傳送,錄像保存。
    幀同步游戲的網(wǎng)絡同步和錄像保存就可以這樣實現(xiàn)。
  3. 方便管理,很容易實現(xiàn)撤銷和重做。

簡單實現(xiàn)撤銷和重做

修改下接口Command

interface Command{
    execute();
    undo();
    redo();
}

保存命令成隊列,設置記錄當前命令index

// 撤銷
command_queue[index--].undo();
// 重做
command_queue[index++].redo();

使用小結

模式的重點:

  1. 對函數(shù)調用做封裝,完成解耦。
  2. 把函數(shù)調用轉換成命令對象,相當于把調用執(zhí)行轉換成數(shù)據(jù)指令,方便后續(xù)各種管理。

不要使用的情況:

  1. 如果只是要解耦下,做一層調用封裝就行了。
  2. 想要擴展命令,多定義幾個調用分裝也就行了。
  3. 如果是立即調用執(zhí)行,沒必要轉成命令模式使用。

使用的情況:只在有后續(xù)管理需求時使用

  1. 需要延后調用,如網(wǎng)絡請求,文件加載等耗時長的任務。使用異步線程處理命令隊列比較好。
  2. 需要保存,做分發(fā)傳輸記錄。
  3. 需要撤銷重做的功能。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 1 場景問題# 1.1 如何開機## 估計有些朋友看到這個標題會非常奇怪,電腦裝配好了,如何開機?不就是按下啟動按...
    七寸知架構閱讀 2,887評論 1 59
  • 3.5 隊列請求## 所謂隊列請求,就是對命令對象進行排隊,組成工作隊列,然后依次取出命令對象來執(zhí)行。多用多線程或...
    七寸知架構閱讀 2,124評論 4 53
  • 目錄 本文的結構如下: 什么是命令模式 為什么要用該模式 模式的結構 代碼示例 優(yōu)點和缺點 適用環(huán)境 模式應用 總...
    w1992wishes閱讀 1,234評論 2 9
  • 轉至元數(shù)據(jù)結尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,041評論 0 9
  • 工廠模式類似于現(xiàn)實生活中的工廠可以產(chǎn)生大量相似的商品,去做同樣的事情,實現(xiàn)同樣的效果;這時候需要使用工廠模式。簡單...
    舟漁行舟閱讀 8,118評論 2 17

友情鏈接更多精彩內容