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

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

場(chǎng)景


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

image

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

判斷每個(gè)插槽對(duì)應(yīng)的具體廠商是誰(shuí), 然后做出對(duì)應(yīng)的動(dòng)作

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

如何解決


對(duì)象村餐廳的例子

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

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

從餐廳到命令模式

image

命令模式總覽


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

  • 類(lèi)圖
image
  • 模式的理解

    • 角色

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

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

        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í)行撤銷(xiāo)命令
          }
        }
        
- "Party模式" :  封裝一次請(qǐng)求中的批量操作 (按下一個(gè)按鈕,同時(shí)弄暗燈光 , 打開(kāi)音響和電視,設(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();
        }
    }
  }
  ```
  • 使用場(chǎng)景

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

案例代碼部分

image
  • 命令對(duì)象Command

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

    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);
      }
    }
    
  • 具體命令對(duì)象 --關(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();    //撤銷(xiāo)操作
          System.out.println(remoteControl);      //此時(shí)應(yīng)該是開(kāi)燈狀態(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%       //撤銷(xiāo)操作 ==> 開(kāi)燈狀態(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
    

參考

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

? 代碼參考地址: 我就是那個(gè)地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容