設(shè)計模式學(xué)習(xí)專欄五--------命令模式

設(shè)計模式學(xué)習(xí)專欄五--------命令模式

場景


設(shè)計一個智能遙控器,遙控器上有7個插槽 , 每個插槽(某設(shè)備具體供應(yīng)商)對應(yīng)兩個按鈕 on , off ,以及一個全局的撤銷操作undo.

image

較差的實(shí)現(xiàn)方式

判斷每個插槽對應(yīng)的具體廠商是誰, 然后做出對應(yīng)的動作

if(slot1 == Light){
    light.on()
}else if(slot1 == Hottub){
    hottub.on()
}else if(slot1 == TV){
    tv.on();
}
...
  • 出現(xiàn)的問題
    • 遙控器和具體的設(shè)備廠商耦合到了一起 , 遙控器需要 認(rèn)識 某個插槽當(dāng)前對應(yīng)設(shè)備的廠商
    • 當(dāng)加入/刪除 新的設(shè)備廠商時, 當(dāng)前代碼都需要進(jìn)行改動

如何解決


對象村餐廳的例子

image
  • 一張訂單封裝了 準(zhǔn)備餐點(diǎn)的請求
  • 女招待的工作是接收訂單 , 然后調(diào)用訂單的OrderUp()方法 , 女招待不需要擔(dān)心訂單的內(nèi)容是什么, 或者由誰來準(zhǔn)備餐點(diǎn) , 她只需要指導(dǎo),訂單由一個OrderUp()方法可以調(diào)用即可.
  • 快餐廚師具有準(zhǔn)備餐點(diǎn)的知識 . 他只要看到訂單就知道如何準(zhǔn)備餐點(diǎn) . 廚師和女招侍之間從不需要直接溝通.

把餐廳想象成設(shè)計模式的一種模型 , 這個模型允許通過"封裝請求的命令"(訂單)將 "發(fā)出請求的對象"(女招待, "訂單來啦")"接收與執(zhí)行這些請求的對象"(廚師) 分隔開 .

從餐廳到命令模式

image

命令模式總覽


定義: 將請求封裝成對象(訂單),將發(fā)出請求的對象(服務(wù)員/按鈕)和執(zhí)行請求的對象(廚師/具體供應(yīng)商)解耦 , 也可以支持撤銷操作

  • 類圖
image
  • 模式的理解

    • 角色

      • 封裝請求的命令對象(訂單) : 一個命令對象通過在特定接收者上綁定一組動作來封裝一個請求 receiver.action1(), 該對象之暴露一個execute()方法, 當(dāng)此方法被調(diào)用時, 接收者就會進(jìn)行對應(yīng)的動作 . 從女招待的角度來看, 它不需要知道具體哪個 接收者進(jìn)行了什么動作,只知道如果調(diào)用execute()方法 , 請求的目的就能達(dá)到了
      • 發(fā)出請求的對象(女招待) : ①負(fù)責(zé)接收命令對象 ②在合適的時候調(diào)用命令對象的execute()方法
      • 執(zhí)行請求的對象(廚師) : 真正執(zhí)行請求的接收者
    • 細(xì)節(jié)

      • "撤銷操作" 只需在Invocker中定義變量Command undoCommand保存撤銷操作即可 . 如果要聯(lián)系撤銷, 則可以用的形式存儲執(zhí)行過的命令

        public class LightOnCommand implements Command {
          Light light;
        
          public LightOnCommand(Light light) {
              this.light = light;
          }
        
          public void execute() {
              light.on();
          }
        
          public void undo() {
              light.off();
          }
        }
        
        public class RemoteControlWithUndo {
          Command[] onCommands;
          Command[] offCommands;
          Command undoCommand;    //記錄上一次命令
         
          public RemoteControlWithUndo() {
              onCommands = new Command[7];
              offCommands = new Command[7];
         
              Command noCommand = new NoCommand();
              for(int i=0;i<7;i++) {
                  onCommands[i] = noCommand;
                  offCommands[i] = noCommand;
              }
              undoCommand = noCommand;
          }
          
          public void setCommand(int slot, Command onCommand, Command offCommand) {
              onCommands[slot] = onCommand;
              offCommands[slot] = offCommand;
          }
         
          public void onButtonWasPushed(int slot) {
              onCommands[slot].execute();
              undoCommand = onCommands[slot]; //記錄上一次命令
          }
         
          public void offButtonWasPushed(int slot) {
              offCommands[slot].execute();
              undoCommand = offCommands[slot];    //記錄上一次命令
          }
         
          public void undoButtonWasPushed() {
              undoCommand.undo(); //執(zhí)行撤銷命令
          }
        }
        
- "Party模式" :  封裝一次請求中的批量操作 (按下一個按鈕,同時弄暗燈光 , 打開音響和電視,設(shè)置好DVD)

  ```java
  public class MacroCommand implements Command {
    Command[] commands;
   
    public MacroCommand(Command[] commands) {
        this.commands = commands;
    }
   
    public void execute() {
        for (int i = 0; i < commands.length; i++) {
            commands[i].execute();
        }
    }
  }
  ```
  • 使用場景

    • 隊列請求 (日志安排 , 線程池 , 工作隊列)
      • 以線程池為例 , 將線程需要執(zhí)行的動作封裝進(jìn)Runnable對象中
      • 線程池 以 阻塞隊列存儲Runnable對象
      • 當(dāng)線程空閑時 , 從隊列中取出Runnable對象 , 并執(zhí)行Runnable中重寫的Run()方法
      • 總結(jié) : 即線程池中的線程不需要知道具體的執(zhí)行者是誰,干了什么,只需要在空閑時從隊列中取出命令對象Runnable并調(diào)用run()方法即可
    • 日志請求
      • 當(dāng)每個命令被執(zhí)行時, 會被儲存在磁盤中
      • 在系統(tǒng)死機(jī)后, 重新加載存儲的命令,并以準(zhǔn)備的次序執(zhí)行

案例代碼部分

image
  • 命令對象Command

    public interface Command {
      public void execute();
      public void undo();
    }
    
  • 具體命令對象--開燈

    public class LightOnCommand implements Command {
      Light light;
      int level;
      public LightOnCommand(Light light) {
          this.light = light;
      }
     
      public void execute() {
            level = light.getLevel();
          light.on();
      }
     
      public void undo() {
          light.dim(level);
      }
    }
    
  • 具體命令對象 --關(guān)燈

    public class LightOffCommand implements Command {
      Light light;
      int level;
      public LightOffCommand(Light light) {
          this.light = light;
      }
     
      public void execute() {
            level = light.getLevel();
          light.off();
      }
     
      public void undo() {
          light.dim(level);
      }
    }
    
  • 設(shè)備廠商 -- Receiver

    public class Light {
      String location;
      int level;
    
      public Light(String location) {
          this.location = location;
      }
    
      public void on() {
          level = 100;
          System.out.println("Light is on");
      }
    
      public void off() {
          level = 0;
          System.out.println("Light is off");
      }
    
      public void dim(int level) {
          this.level = level;
          if (level == 0) {
              off();
          }
          else {
              System.out.println("Light is dimmed to " + level + "%");
          }
      }
    
      public int getLevel() {
          return level;
      }
    }
    
  • 調(diào)用者Invoker --遠(yuǎn)程遙控器

    public class RemoteControlWithUndo {
      Command[] onCommands;
      Command[] offCommands;
      Command undoCommand;
     
      public RemoteControlWithUndo() {
          onCommands = new Command[7];
          offCommands = new Command[7];
     
          Command noCommand = new NoCommand();
          for(int i=0;i<7;i++) {
              onCommands[i] = noCommand;
              offCommands[i] = noCommand;
          }
          undoCommand = noCommand;
      }
      
      public void setCommand(int slot, Command onCommand, Command offCommand) {
          onCommands[slot] = onCommand;
          offCommands[slot] = offCommand;
      }
     
      public void onButtonWasPushed(int slot) {
          onCommands[slot].execute();
          undoCommand = onCommands[slot];
      }
     
      public void offButtonWasPushed(int slot) {
          offCommands[slot].execute();
          undoCommand = offCommands[slot];
      }
     
      public void undoButtonWasPushed() {
          undoCommand.undo();
      }
    }
    
  • 主程序

    public class RemoteLoader {
    
      public static void main(String[] args) {
          RemoteControlWithUndo remoteControl = new RemoteControlWithUndo();
    
          Light livingRoomLight = new Light("Living Room");
    
          LightOnCommand livingRoomLightOn =
                  new LightOnCommand(livingRoomLight);
          LightOffCommand livingRoomLightOff =
                  new LightOffCommand(livingRoomLight);
    
          remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff);
    
          remoteControl.onButtonWasPushed(0);
          remoteControl.offButtonWasPushed(0);
          System.out.println(remoteControl);
          remoteControl.undoButtonWasPushed();    //撤銷操作
          System.out.println(remoteControl);      //此時應(yīng)該是開燈狀態(tài)
      }
    }
    
  • 輸出結(jié)果

    Light is on
    Light is off
    
    ------ Remote Control -------
    [slot 0] headfirst.designpatterns.command.undo.LightOnCommand    headfirst.designpatterns.command.undo.LightOffCommand
    [slot 1] headfirst.designpatterns.command.undo.NoCommand    headfirst.designpatterns.command.undo.NoCommand
    [slot 2] headfirst.designpatterns.command.undo.NoCommand    headfirst.designpatterns.command.undo.NoCommand
    [slot 3] headfirst.designpatterns.command.undo.NoCommand    headfirst.designpatterns.command.undo.NoCommand
    [slot 4] headfirst.designpatterns.command.undo.NoCommand    headfirst.designpatterns.command.undo.NoCommand
    [slot 5] headfirst.designpatterns.command.undo.NoCommand    headfirst.designpatterns.command.undo.NoCommand
    [slot 6] headfirst.designpatterns.command.undo.NoCommand    headfirst.designpatterns.command.undo.NoCommand
    [undo] headfirst.designpatterns.command.undo.LightOffCommand
    
    Light is dimmed to 100%       //撤銷操作 ==> 開燈狀態(tài)
    
    ------ Remote Control -------
    [slot 0] headfirst.designpatterns.command.undo.LightOnCommand    headfirst.designpatterns.command.undo.LightOffCommand
    [slot 1] headfirst.designpatterns.command.undo.NoCommand    headfirst.designpatterns.command.undo.NoCommand
    [slot 2] headfirst.designpatterns.command.undo.NoCommand    headfirst.designpatterns.command.undo.NoCommand
    [slot 3] headfirst.designpatterns.command.undo.NoCommand    headfirst.designpatterns.command.undo.NoCommand
    [slot 4] headfirst.designpatterns.command.undo.NoCommand    headfirst.designpatterns.command.undo.NoCommand
    [slot 5] headfirst.designpatterns.command.undo.NoCommand    headfirst.designpatterns.command.undo.NoCommand
    [slot 6] headfirst.designpatterns.command.undo.NoCommand    headfirst.designpatterns.command.undo.NoCommand
    [undo] headfirst.designpatterns.command.undo.LightOffCommand
    

參考

? 書籍: HeadFirst設(shè)計模式

? 代碼參考地址: 我就是那個地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末织狐,一起剝皮案震驚了整個濱河市驱入,隨后出現(xiàn)的幾起案子径密,更是在濱河造成了極大的恐慌诱鞠,老刑警劉巖油坝,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件葫辐,死亡現(xiàn)場離奇詭異梅肤,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)沐序,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門琉用,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人策幼,你說我怎么就攤上這事邑时。” “怎么了特姐?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵晶丘,是天一觀的道長。 經(jīng)常有香客問我浅浮,道長脑题,這世上最難降的妖魔是什么叔遂? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任哩掺,我火速辦了婚禮盒件,結(jié)果婚禮上誊稚,老公的妹妹穿的比我還像新娘。我一直安慰自己脖镀,他們只是感情好漫萄,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布削饵。 她就那樣靜靜地躺著窿撬,像睡著了一般劈伴。 火紅的嫁衣襯著肌膚如雪密末。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天跛璧,我揣著相機(jī)與錄音严里,去河邊找鬼。 笑死追城,一個胖子當(dāng)著我的面吹牛刹碾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播座柱,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼迷帜,長吁一口氣:“原來是場噩夢啊……” “哼物舒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起戏锹,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤冠胯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后景用,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體涵叮,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年伞插,在試婚紗的時候發(fā)現(xiàn)自己被綠了割粮。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡媚污,死狀恐怖舀瓢,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情耗美,我是刑警寧澤京髓,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站商架,受9級特大地震影響堰怨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蛇摸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一备图、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧赶袄,春花似錦揽涮、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至敬辣,卻和暖如春雪标,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背溉跃。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工汰聋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人喊积。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓烹困,卻偏偏與公主長得像,于是被迫代替她去往敵國和親乾吻。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評論 2 345