iT邦幫忙

2023 iThome 鐵人賽

DAY 22
0
自我挑戰組

深入淺出設計模式 (Head First Design Pattern) - 重點整理及範例分享系列 第 22

[深入淺出設計模式] Ch6 The Command Pattern (3) - 【命令模式】範例實作 範例 Home Automation Remote Control

  • 分享至 

  • xImage
  •  

命令模式

前面提過命令模式的概念,感覺還是挺抽象的,所以書中有更好懂的譬喻,假設今天我們到一個餐廳用餐,首先我們 Client 點餐時會先看菜單選項 ,決定好再叫服務生Invoker 來進行點餐setCommand() ,服務生會把客人的訂單內容 Command 送到廚房 execute() 給廚師 Receiver 去準備餐點,準備好後再由服務生送到我們 Client 手上。
命令模式的精髓在於,廚師不用知道是誰點了A餐,但是身為 Receiver 的他知道如何煮出那個餐點,也就是在命令模式中要如何執行那個功能,相對的,服務生也就是 Invoker 不用知道怎麼煮,只要負責中間的餐點傳遞。

實作範例 Home Automation Remote Control 家電智能遙控

分析一下,我們依據上篇需求對應到命令模式的角色為何:

  1. Client: 使用者
  2. Invoker: 遙控器
  3. setCommand(): 按鈕本身
  4. Command: 按鈕對應的功能
  5. Receiver: 家電或其他裝置
  6. execute(): 按下按鈕的動作

以下用部分程式碼來講解,完整版在這
首先我們的 Receiver 家電 Light() 就像廚師,只有他知道功能要怎麼實行,所以把開關或是其他動作的方法定義在這裡:

abstract public class Receiver {   
    public String position;
    // some action methods here
}
public class Light extends Receiver{
    public Light(String position){
        this.position = position;
    }
    public void On(){
        System.out.println("Turn on the light in " + position );
    }
    public void Off(){
        System.out.println("Turn on the light in " + position );
    }   
}

Command負責執行功能,但不知道詳細執行細節:

public interface Command{
    public void execute(Receiver receiver);
}
//不同裝置的不同動作(e.g. 電燈開或關)都會實作 Command() 介面
public class LightOnCommand{
    public void execute(Light light){        
        light.On();
    }
}
public class LightOffCommand implements Command{
    Light light;

    public LightOffCommand(Light light){
        this.light = light;
    }
    public void execute(){        
        light.Off();
    }
}

Invoker 遙控器 Remote() 可以設定不同按鈕連結到的功能,但他不知道功能是什麼,只知道包含開 Command onCommand 和關 Command offCommand

public class Remote extends Invoker{
    ArrayList<Command> onCommands;
    ArrayList<Command> offCommands;

    public Remote(){
        onCommands = new ArrayList<Command>(8);
        offCommands = new ArrayList<Command>(8);
    }
    
    //在對應的數字設定開和關的按鈕,不需了解執行細節,只用到 Command() 介面
    public void setCommand(int btnVal, Command onCommand, Command offCommand){
        onCommands.add(btnVal - 1, onCommand);
        offCommands.add(btnVal - 1, offCommand);        
    }

    public void pressOnBtn(int btnVal){
        onCommands.get(btnVal - 1).execute();
    }

    public void pressOffBtn(int btnVal){
        offCommands.get(btnVal - 1).execute();
    }
}

先測試一下功能:

public class CommandPattern {
    public static void main(String[] args) {
        Remote remote = new Remote();
        Light livingroomLight = new Light("living room");
        Light studioLight = new Light("Music Studio");

        Curtain bedroomCurtain = new Curtain("bedroom");
        Curtain livingroomCurtain = new Curtain("Livingroom");
        Fridge kitchenFridge = new Fridge("kitchen");

        CurtainOpenCommand livingroomCurtainOpen = new CurtainOpenCommand(livingroomCurtain);
        CurtainCloseCommand livingroomCurtainClose = new CurtainCloseCommand(livingroomCurtain);

        CurtainOpenCommand bedroomCurtainOpen = new CurtainOpenCommand(bedroomCurtain);
        CurtainCloseCommand bedroomCurtainClose = new CurtainCloseCommand(bedroomCurtain);

        LightOnCommand livingroomLightOnCommand = new LightOnCommand(livingroomLight);
        LightOffCommand livingroomLightOffCommand = new LightOffCommand(livingroomLight);

        LightOnCommand studioLightOnCommand = new LightOnCommand(studioLight);
        LightOffCommand studioLightOffCommand = new LightOffCommand(studioLight);

        FridgeOpenCommand kitchenFridgeOpenCommand = new FridgeOpenCommand(kitchenFridge);
        FridgeCloseCommand kitchenFridgeOffCommand = new FridgeCloseCommand(kitchenFridge);

        remote.setCommand(1, livingroomCurtainOpen, livingroomCurtainClose);
        remote.setCommand(2, livingroomLightOnCommand, livingroomLightOffCommand);
        remote.setCommand(3, kitchenFridgeOpenCommand, kitchenFridgeOffCommand);
        remote.setCommand(4, bedroomCurtainOpen, bedroomCurtainClose);
        remote.setCommand(5, studioLightOnCommand, studioLightOffCommand);

        remote.pressOffBtn(4);
        remote.pressOnBtn(5);
        remote.pressOnBtn(1);
        remote.pressOnBtn(3);
    }
}

輸出:
https://ithelp.ithome.com.tw/upload/images/20231008/20163178ysDj7RUkzO.png

下篇繼續做完 global 的取消功能 →


參考資料:

  1. 《深入淺出設計模式 (Head First Design Patterns) 》
  2. 書中官方程式碼傳送門
  3. 本篇完整程式碼傳送門

Disclaimer
因為讀的是原文版,所以難免會有翻譯詞不達意或是專有名詞上的差異,有錯誤的話歡迎在留言區一起交流!


上一篇
[深入淺出設計模式] Ch6 The Command Pattern (2) - 【命令模式】範例 Home Automation Remote Control
下一篇
[深入淺出設計模式] Ch6 The Command Pattern (4) - 【Meta Command Pattern】範例實作
系列文
深入淺出設計模式 (Head First Design Pattern) - 重點整理及範例分享35
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言