对命令模式的理解

server/2024/9/24 13:18:43/

目录

  • 一、场景
    • 1、文本编辑器并不是一个好的例子,设备控制器才是
    • 2、设备控制器的demo
  • 二、不用命令模式
    • 1、代码
    • 2、问题
  • 三、使用命令模式
    • 1、代码
    • 2、当需求变化时
      • 2.1 新增代码
      • 2.2 优点
  • 四、进一步思考
    • 1、省略对Command的建模可以吗?
    • 2、命令模式的价值

一、场景

  • 脱离场景谈设计模式毫无意义。先有某种场景,然后大神们(GoF)对其进行建模,以实现高内聚低耦合。

1、文本编辑器并不是一个好的例子,设备控制器才是

  • 看了不少讲命令模式的文章,举的例子基本都是文本编辑器。
  • 然而,并没有揭示命令模式的精髓。反而让读者觉得命令模式像过度设计。
  • 生活中,我们手机上会装一个设备控制器,里面既可以控制空调的开关,还可以控制电视的开关。我将通过这个例子来阐述命令模式到底解决了什么问题

2、设备控制器的demo

  • 用户选择空调,并选择关闭:空调便关闭了。
  • 用户选择电视机,并选择关闭:电视机便关闭了。

二、不用命令模式

1、代码

  • 设备:
public class AirConditioner {public void turnOn() {System.out.println("空调已开启");}public void turnOff() {System.out.println("空调已关闭");}
}public class Television {public void turnOn() {System.out.println("电视已开启");}public void turnOff() {System.out.println("电视已关闭");}
}
  • 客户端:
public class Application {public static void main(String[] args) {AirConditioner airConditioner = new AirConditioner();Television television = new Television();Scanner scanner = new Scanner(System.in);while (true) {String device = scanner.next();if (device.equals("exit")) {break;}String command = scanner.next();if (device.equals("airConditioner")) {if (command.equals("turnOn")) {airConditioner.turnOn();} else if (command.equals("turnOff")) {airConditioner.turnOff();}} else if (device.equals("television")) {if (command.equals("turnOn")) {television.turnOn();} else if (command.equals("turnOff")) {television.turnOff();}}}}
}
  • 结果:
    在这里插入图片描述

2、问题

  • 随着发展,设备大概率不止空调和电视机,一旦增加新设备,客户端的代码就要有很大的改动。而且,每台设备能执行的命令也不止开启和关闭,一旦新增命令,又要修改客户端的代码。这都违背了“开闭原则”。
  • 另外,各种设备的代码杂糅在一起,违背了“单一职责原则”

三、使用命令模式

1、代码

  • 设备:
public interface Receiver {void turnOn();void turnOff();
}public class AirConditioner implements Receiver {@Overridepublic void turnOn() {System.out.println("空调已开启");}@Overridepublic void turnOff() {System.out.println("空调已关闭");}
}public class Television implements Receiver {@Overridepublic void turnOn() {System.out.println("电视已开启");}@Overridepublic void turnOff() {System.out.println("电视已关闭");}
}
  • 命令:
public abstract class Command {protected Receiver receiver;public void setReceiver(Receiver receiver) {this.receiver = receiver;}public abstract void execute();
}public class TurnOffCommand extends Command {@Overridepublic void execute() {receiver.turnOff();}
}public class TurnOnCommand extends Command {@Overridepublic void execute() {receiver.turnOn();}
}
  • 设备管理器:DeviceManager
public class DeviceManager {private final Map<String, Receiver> devices;private final Map<String, Command> commands;private DeviceManager() {devices = ImmutableMap.of(DeviceEnum.AIR_CONDITIONER.getValue(), new AirConditioner(),DeviceEnum.TELEVISION.getValue(), new Television());commands = ImmutableMap.of(CommandEnum.TURN_ON.getValue(), new TurnOnCommand(),CommandEnum.TURN_OFF.getValue(), new TurnOffCommand());}public static DeviceManager getSingleton() {return new DeviceManager();}public void execute(String device, String commandType) {Receiver receiver = devices.get(device);Command command = commands.get(commandType);command.setReceiver(receiver);command.execute();}
}
  • 客户端:
public class Application {public static void main(String[] args) {DeviceManager deviceManager = DeviceManager.getSingleton();Scanner scanner = new Scanner(System.in);while (true) {String device = scanner.next();if (device.equals("exit")) {break;}String command = scanner.next();deviceManager.execute(device, command);}}
}

2、当需求变化时

  • 新增一个设备:洗衣机
  • 新增一个命令:待机

2.1 新增代码

  • 新增命令:
public class StandByCommand extends Command {@Overridepublic void execute() {receiver.standby();}
}
  • 新增设备:
public interface Receiver {...void standby(); // 新增方法
}// 新增设备
public class Washer implements Receiver {@Overridepublic void turnOn() {System.out.println("洗衣机已开启");}@Overridepublic void turnOff() {System.out.println("洗衣机已关闭");}@Overridepublic void standby() {System.out.println("洗衣机已待机");}
}
  • 修改设备(新增方法):
public class AirConditioner implements Receiver {...@Overridepublic void standby() {System.out.println("空调已待机");}
}public class Television implements Receiver {...@Overridepublic void standby() {System.out.println("电视已待机");}
}
  • 修改设备管理器:
public class DeviceManager {private final Map<String, Receiver> devices;private final Map<String, Command> commands;private DeviceManager() {devices = ImmutableMap.of(...DeviceEnum.WASHER.getValue(), new Washer());commands = ImmutableMap.of(...CommandEnum.STAND_BY.getValue(), new StandByCommand());}...
}

2.2 优点

  • 之前写的代码,一行都没改动。只是通过新增代码,便实现了需求:
    在这里插入图片描述
    • 尽可能符合“开闭原则”。
  • 各个设备、各个命令都很单一,尽可能符合“单一职责”。

四、进一步思考

1、省略对Command的建模可以吗?

  • 省略后的伪代码:
public class DeviceManager {...public void execute(String device, String commandType) {// 根据device找到对应的实体Device realDevice =	deviceMap.get(device);// 每个device根据commandType执行不同的逻辑realDevice.execute(commandType);}...
}// 各个device都要实现这样的路由逻辑:
public void execute(String commandType) {if ("turnOn".equals(commandType)) {...} else if ("turnOff".equals(commandType)) {...} else {...}
}
  • 显然,去除对Command的建模后,代码变得冗余了。

2、命令模式的价值

  • 当对真实场景建模后,各部分的交互逻辑如下图所示,便可以采用命令模式实现“高内聚、低耦合”的代码设计。
    在这里插入图片描述

http://www.ppmy.cn/server/32283.html

相关文章

2024五一数学建模B题思路代码与论文分析

2024五一数学建模B题完整代码和成品论文获取↓↓↓↓↓ https://www.yuque.com/u42168770/qv6z0d/gyoz9ou5upvkv6nx?singleDoc# B题 未来新城交通需求规划与可达率问题需要建立的模型和算法: 1. 图论 2. 网络流模型 3. 线性规划/整数规划 4. 组合优化 5. 随机过程 6. …

C#调用skiasharp操作并绘制图片

之前学习ViewFaceCore时采用Panel控件和GDI将图片及识别出的人脸方框和关键点绘制出来&#xff0c;本文将其修改为基于SKControl和SKCanvas实现相同的显示效果并支持保存为本地图片。   新建Winform项目&#xff0c;在Nuget包管理器中搜索并安装一下SkiaSharp和ViewFaceCore…

STM32 F103C8T6学习笔记17:类IIC通信(SMBus协议)—MLX90614红外非接触温度计

今日学习配置MLX90614红外非接触温度计 与 STM32 F103C8T6 单片机的通信 文章提供测试代码讲解、完整工程下载、测试效果图 本文需要用到的大概基础知识&#xff1a;1.3寸OLED配置通信显示、IIC通信、 定时器配置使用 这里就只贴出我的 OLED驱动方面的网址链接了&#xff1a…

code-server容器webpack的ws无法连接解决方法

TLDR 通过指定client的wsrul去连接ws devServer.client.webSocketURL ‘wss://<Forwarded uri>/ws’ 拓扑 1、code-server: 用于编写代码、启动webpack dev-server 服务&#xff1b;[https://<domain>:8001] 2、webpack: 用于浏览dev-server服务&#xff1b;[ht…

2011NOIP普及组真题 2. 统计单词数

线上OJ&#xff1a; 一本通&#xff1a;http://ybt.ssoier.cn:8088/problem_show.php?pid1954 核心思想 1、本题中比较单词不考虑大小写&#xff0c;所以在比较前先统一转换为小写或者大写。然后再比较即可。 2、由于 s2 会有前导空格&#xff0c;且可能单词之间的空格不止1个…

用 Go struct 不能犯的一个低级错误!

疑惑的例子 其给出的例子一如下&#xff1a; type People struct {}func main() {a : &People{}b : &People{}fmt.Println(a b) }你认为输出结果是什么呢&#xff1f; 输出结果是&#xff1a;false。 再稍加改造一下&#xff0c;例子二如下&#xff1a; type Peo…

代码审计之SAST自动化

前言: 很久没写文章了&#xff0c;有点忙&#xff0c;落个笔&#xff0c;分享一些捣鼓或说适配好的一些好玩的东西。 脚本工具不开源&#xff0c;给一些思路&#xff0c;希望能给大家带来一些收获。 笔者能力有限&#xff0c;如有错误&#xff0c;欢迎斧正。 正文&#xff1a…

phpMyAdmin增加自定义IP登录教程

phpMyAdmin增加自定义IP登录教程 1、打开phpMyAdmin目录&#xff0c; 在此目录下是否有config.sample.inc.php文件&#xff0c;如果存在&#xff0c;那么将其改名为config.inc.php&#xff08;为避免修改失误所造成的损失&#xff0c;强烈建议先备份config.sample.inc.php文件…