【HeadFirst系列之HeadFirst设计模式】第7天之命令模式:封装请求,轻松实现解耦!

news/2025/2/22 22:25:06/

命令模式:封装请求,轻松实现解耦!

大家好!今天我们来聊聊设计模式中的命令模式(Command Pattern)。如果你曾经需要将请求封装成对象,或者希望实现请求的撤销、重做等功能,那么命令模式就是你的不二之选!本文基于《Head First 设计模式》的命令模式章节,通过生动的故事和 Java 代码示例,带你轻松掌握命令模式的精髓。

在这里插入图片描述


1. 命令模式是什么?

命令模式是一种行为型设计模式,它将请求封装成对象,从而使你可以用不同的请求对客户进行参数化,并支持请求的排队、记录日志、撤销等操作。命令模式的核心思想是解耦请求的发送者和接收者,使得系统更加灵活和可扩展。

适用场景

  • 需要将请求封装成对象,以便在不同的上下文中使用。
  • 需要支持请求的撤销、重做、排队等功能。
  • 需要解耦请求的发送者和接收者。

2. 命令模式的实现

故事背景

小明开发了一个智能家居系统,系统中有一个遥控器(RemoteControl)类,用于控制各种家电设备,比如(Light)、风扇(Fan)等。每个设备都有不同的操作,比如打开、关闭、调节亮度等。

问题出现

如果直接在遥控器中调用设备的方法,会导致遥控器和设备之间的耦合度过高。此外,如果需要支持撤销操作,代码会变得非常复杂。

解决方案:命令模式

小明决定使用命令模式,将每个操作封装成一个命令对象,从而解耦遥控器和设备。

代码实现

1. 定义命令接口
// 命令接口
interface Command {void execute();void undo();
}
2. 实现具体命令
// 具体命令:打开灯
class LightOnCommand implements Command {private Light light;public LightOnCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.on();}@Overridepublic void undo() {light.off();}
}// 具体命令:关闭灯
class LightOffCommand implements Command {private Light light;public LightOffCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.off();}@Overridepublic void undo() {light.on();}
}// 具体命令:打开风扇
class FanOnCommand implements Command {private Fan fan;public FanOnCommand(Fan fan) {this.fan = fan;}@Overridepublic void execute() {fan.on();}@Overridepublic void undo() {fan.off();}
}// 具体命令:关闭风扇
class FanOffCommand implements Command {private Fan fan;public FanOffCommand(Fan fan) {this.fan = fan;}@Overridepublic void execute() {fan.off();}@Overridepublic void undo() {fan.on();}
}
3. 定义设备类
// 灯类
class Light {public void on() {System.out.println("Light is on");}public void off() {System.out.println("Light is off");}
}// 风扇类
class Fan {public void on() {System.out.println("Fan is on");}public void off() {System.out.println("Fan is off");}
}
4. 实现遥控器
// 遥控器类
class RemoteControl {private Command[] onCommands;private Command[] offCommands;private Command undoCommand;public RemoteControl() {onCommands = new Command[2];offCommands = new Command[2];Command noCommand = new NoCommand();for (int i = 0; i < 2; 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();}
}// 空命令类
class NoCommand implements Command {@Overridepublic void execute() {System.out.println("No command assigned");}@Overridepublic void undo() {System.out.println("No command assigned");}
}
5. 客户端代码
public class SmartHomeApp {public static void main(String[] args) {// 创建设备Light livingRoomLight = new Light();Fan livingRoomFan = new Fan();// 创建命令Command lightOn = new LightOnCommand(livingRoomLight);Command lightOff = new LightOffCommand(livingRoomLight);Command fanOn = new FanOnCommand(livingRoomFan);Command fanOff = new FanOffCommand(livingRoomFan);// 创建遥控器RemoteControl remoteControl = new RemoteControl();remoteControl.setCommand(0, lightOn, lightOff);remoteControl.setCommand(1, fanOn, fanOff);// 操作遥控器remoteControl.onButtonWasPushed(0); // 输出: Light is onremoteControl.offButtonWasPushed(0); // 输出: Light is offremoteControl.undoButtonWasPushed(); // 输出: Light is onremoteControl.onButtonWasPushed(1); // 输出: Fan is onremoteControl.offButtonWasPushed(1); // 输出: Fan is offremoteControl.undoButtonWasPushed(); // 输出: Fan is on}
}

3. 命令模式的优点

  1. 解耦请求的发送者和接收者
    命令模式将请求封装成对象,使得请求的发送者和接收者之间没有直接的依赖关系。

  2. 支持撤销和重做
    通过实现 undo() 方法,可以轻松实现撤销操作。

  3. 支持请求的排队和日志记录
    命令对象可以被存储、传递和记录,从而支持请求的排队和日志记录。

  4. 易于扩展
    新增命令时,只需实现新的命令类,无需修改现有代码。


4. 总结

命令模式通过将请求封装成对象,实现了请求的发送者和接收者之间的解耦,从而使得系统更加灵活和可扩展。通过本文的讲解和代码示例,相信你已经掌握了命令模式的核心思想和实现方法。在实际开发中,命令模式非常适合用于实现撤销、重做、排队等功能。


互动话题
你在项目中用过命令模式吗?遇到过哪些问题?欢迎在评论区分享你的经验!


http://www.ppmy.cn/news/1574262.html

相关文章

R语言NIMBLE、Stan和INLA贝叶斯平滑及条件空间模型死亡率数据分析:提升疾病风险估计准确性...

全文链接&#xff1a;https://tecdat.cn/?p40365 在环境流行病学研究中&#xff0c;理解空间数据的特性以及如何通过合适的模型分析疾病的空间分布是至关重要的。本文主要介绍了不同类型的空间数据、空间格点过程的理论&#xff0c;并引入了疾病映射以及对空间风险进行平滑处理…

Jupyter里面的manim编程学习

1.Jupyterlab的使用 因为我之前一直都是使用的vscode进行manim编程的&#xff0c;但是今天看的这个教程使用的是Jupyter&#xff0c;我也很是好奇这个manim在Jupyter这样的交互式下面会生成怎么样的效果&#xff0c;所以今天尝试了jupyter&#xff0c;并且对于两个进行比较和说…

OpenSSL实验

文章目录 一、OpenSSL安装二、OpenSSL配置常见路径查找配置文件的方法示例**1. 配置文件结构****2. 主要段落及其作用****(1) 默认段&#xff08;Default Section&#xff09;****(2) OID段&#xff08;OID Section&#xff09;****(3) CA相关段&#xff08;CA Section&#xf…

C++ 设计模式-备忘录模式

游戏存档实现&#xff0c;包括撤销/重做、持久化存储、版本控制和内存管理 #include <iostream> #include <memory> #include <deque> #include <stack> #include <chrono> #include <fstream> #include <sstream> #include <ct…

在低功耗MCU上实现人工智能和机器学习

作者&#xff1a;Silicon Labs 人工智能&#xff08;AI&#xff09;和机器学习&#xff08;ML&#xff09;技术不仅正在快速发展&#xff0c;还逐渐被创新性地应用于低功耗的微控制器&#xff08;MCU&#xff09;中&#xff0c;从而实现边缘AI/ML解决方案。这些MCU是许多嵌入式…

html中iframe标签 隐藏滚动条

iframe 隐藏滚动条 1. 直接隐藏的策略&#xff1a; frame 有个属性 scrolling&#xff0c;直接设置 scrolling‘no’ 即可隐藏 scrollbar。这样iframe就不能滚动了。 <iframe src"https://www.baidu.com/" scrolling"no"></iframe>2. 修改 i…

使用 Docker 部署 Apache Spark 集群教程

简介 Apache Spark 是一个强大的统一分析引擎&#xff0c;用于大规模数据处理。本文将详细介绍如何使用 Docker 和 Docker Compose 快速部署一个包含一个 Master 节点和两个 Worker 节点的 Spark 集群。这种方法不仅简化了集群的搭建过程&#xff0c;还提供了资源隔离、易于扩…

Jmeter进阶篇(34)如何解决jmeter.save.saveservice.timestamp_format=ms报错?

问题描述 今天使用Jmeter完成压测执行,然后使用命令将jtl文件转换成html报告时,遇到了报错! 大致就是说jmeter里定义了一个jmeter.save.saveservice.timestamp_format=ms的时间格式,但是jtl文件中的时间格式不是标准的这个ms格式,导致无法正常解析。对于这个问题,有如下…