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();
}
  • 实现打开电灯的命令
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();
    }
}
  • 使用命令对象:
// 相当于服务员
public class SimpleRemoteControl{
	Command slot; 
    public SimpleRemoteControl(){}
    // 服务员拿走顾客下好的单
    public void setCommand(Command command){ 
        slot = command;
    }
    // 按下按钮则调用该方法(服务员把订单放在厨师台,让厨师开始执行订单)
    public void buttonWasPressed(){  
        slot.execute();
    }
}
  • 测试遥控器:
// 相当于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();  // 按下按钮
    }
}
实现遥控器

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();
    }
}
public class NoCommand implements Command{
    public void excute(){}  // 什么事都不做(可以理解为插槽上没有插入家电,但是依旧可以按下按钮)
    public void undo(){}  // 同理
}
实现命令

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


测试遥控器

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);  // 按下撤销按钮,卧室灯重新打开
    }
}
一个按钮控制多个电器

假设此时想按下一个插槽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);

参考

Head First 设计模式-命令模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值