• Head First设计模式(阅读笔记)-06.命令模式


    家电自动化遥控器

    假设要创建一个遥控器,该遥控器上有7个插槽(每个可以插上不同的家电),每个插槽对应了开关按钮,并且遥控器上还有一个撤销按钮用于撤销上一次的操作


    从餐厅点餐开始

    假设一个顾客来到餐厅要进行点餐,整体流程如下:

    • 顾客点了需要的食物(createOrder),生成了一个订单
    • 服务员拿走订单(takeOrder),放在订单柜台并通知厨师准备(orderUp)
    • 厨师根据订单上的指示进行操作(makeBurger)
    • 做好后顾客就可以开吃了

    • 服务员不必关心订单的内容,只需要接过不同订单(相当于takeOrder中传入不同参数)然后调用订单上的orderUp方法
    • 厨师和服务员不需要沟通(相当于解耦),它只需要看订单上的请求就知道该做什么餐了

    命令模式

    • 命令模式将请求封装为对象,以便使用不同的请求、队列或日志来参数化其他对象
    • 命令对象将动作和接收者包进对象中,该对象只暴露出excute方法,该方法被调用时接收者就进行这些动作,外部不知道进行了哪些动作,只需要知道调用excute方法,请求的目的就能达成(比如插槽不需要知道插入的时什么电器,执行excute方法就行)
    • 命令模式将发出请求的对象和执行请求的对象进行解耦,这两种通过命令对象进行交流
    • 命令可以实现日志和事务系统

    在这里插入图片描述


    分析点餐

    餐厅命令模式
    服务员Invoker
    厨师Receiver
    orderUp()execute()
    订单Command
    顾客Client
    takeOrder()setCommand()

    在这里插入图片描述

    开始设计遥控器

    该部分介绍大致设计步骤


    • 实现命令接口
    // 可以理解为点餐的订单
    public interface Command{
        public void execute();
        public void undo();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 实现打开电灯的命令
    public class LightOnCommand implements Command{
        Light light;  // 有on和off方法(相当于厨师)
        public LightOnCommand(Light light){
            this.light = light;
        }
        // 该订单上写明要的餐(即要求厨师需要执行什么操作)
        public void execute(){
            light.on();  // 电灯变为接收者,负责接收请求
        }
        // 撤销就是做一个相反的操作
        public void undo(){
            light.off();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 使用命令对象:
    // 相当于服务员
    public class SimpleRemoteControl{
    	Command slot; 
        public SimpleRemoteControl(){}
        // 服务员拿走顾客下好的单
        public void setCommand(Command command){ 
            slot = command;
        }
        // 按下按钮则调用该方法(服务员把订单放在厨师台,让厨师开始执行订单)
        public void buttonWasPressed(){  
            slot.execute();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 测试遥控器:
    // 相当于Client
    public class RemoteControlTest{
    	public static void main(String[] args){
            // 遥控器为调用者,传入一个命令对象然后发出请求
            SimpleRemoteControl remote = new SimpleRemoteControl();
        	Light light = new Light();  // 请求的接收者(厨师)
            LightOnCommand lightOn = new LightOnCommand(light);
            remote.setCommand(lightOn);  // 把命令传递给调用者
            remote.buttonWasPressed();  // 按下按钮
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    实现遥控器

    public class RemoteControl{
        Command[] onCommands;
        Command[] offCommands;
        Command undoCommand;
        public RemoteControl(){
            onCommands = new Command[7];  // 7个开按钮
            offCommands = new Command[7];  // 7个关按钮
            // 初始化为一个具体对象(该对象中的excute方法不做任何动作)
            // 这样就不需要每次都先判断onCommands[slot]是否为null再去执行excute方法
            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].excute();
            undoCommand = onCommands[slot];  // 按下按钮后将该操作其记录,需要撤销时可以使用
        }
        // 按下关按钮后,就开始执行一系列的动作
        public void offButtonWasPushed(int slot){
            offCommands[slot].excute();
            undoCommand = offCommands[slot];  // 按下按钮后将该操作其记录,需要撤销时可以使用
        }
        // 按下撤销按钮后,就开始执行上一个命令的一系列动作
        public void undoButtonWasPushed(int slot){
            undoCommand.undo();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    public class NoCommand implements Command{
        public void excute(){}  // 什么事都不做(可以理解为插槽上没有插入家电,但是依旧可以按下按钮)
        public void undo(){}  // 同理
    }
    
    • 1
    • 2
    • 3
    • 4
    实现命令

    参考开始设计遥控器实现打开电灯的命令


    测试遥控器

    public class RemoteLoader{
    	public static void main(String[] args){
            RemoteControl remoteControl = new RemoteControl();
        	Light roomLight = new Light("room");
            LightOnCommand roomLightOn = new LightOnCommand(roomLight);
            LightOffCommand roomLightOff = new LightOffCommand(roomLight);
            remoteControl.setCommand(0, roomLightOn, roomLightOff);
            remoteControl.onButtonWasPushed(0);  // 使用插槽0打开卧室的灯
            remoteControl.offButtonWasPushed(0);  // 使用插槽0关闭卧室的灯
            remoteControl.undoButtonWasPushed(0);  // 按下撤销按钮,卧室灯重新打开
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    一个按钮控制多个电器

    假设此时想按下一个插槽0的开按钮后,同时关闭卧室灯并且打开电视,该如何实现?


    // 包含一系列命令的命令
    public class MarcoCommand implements Command{
        Command[] commands;
        public MarcoCommand(Command[] commands){
            this.commands = commands;
        }
        public void execute(){
            for(int i = 0; i < commands.length; i++){
                commands[i].excute();
            }
        }
        public void undo(){
            for(int i = 0; i < commands.length; i++){
                commands[i].undo();
            }
        }
    }
    // 具体实现 
    Light roomLight = new Light("room");
    TV tv = new TV("tv");
    LightOffCommand roomLightOff = new LightOffCommand(roomLight);
    TVOnCommand tvOn = new TVOnCommand(tv);
    Commands[] roomLightOntvOff = {roomLightOff, tvOn};
    MarcoCommand demo = new MarcoCommand(roomLightOntvOff);
    remoteControl.setCommand(0, demo, null);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    参考

    Head First 设计模式-命令模式

  • 相关阅读:
    docker部署mysql 实现远程连接
    uview+uniapp+springboot 实现小程序上传图片并回显
    三模块七电平级联H桥整流器电压平衡控制策略Simulink仿真
    【scikit-learn基础】--『数据加载』之真实数据集
    java多线程基础技术
    医疗机器人技术研究现状
    Docker 的 Busybox 操作系统镜像
    F28069教程3-中断 PIE
    性能与效果平衡:选择适合项目的直播实时美颜SDK
    领域模型优先于数据库表
  • 原文地址:https://blog.csdn.net/qq_41398418/article/details/128094084