开头说几句
博主的博客地址: https://www.jeffcc.top/
博主学习设计模式用的书是 Head First 的《设计模式》,强烈推荐配套使用!
什么是命令模式
权威定义:将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持撤销操作。
博主的理解:关注的重点应该在于我们如何将一个个请求(命令)封装成对象,然后适配到命令调用者手上,调用者不需要知道具体实现了什么操作,同时实现了将发出请求的对象和接受与执行请求的对象之间的解耦。
设计原则
- 封装变化
- 多用组合,少用继承
- 针对接口编程,不针对实现编程
- 为交互对象之间的松耦合设计而努力
- 类应该对拓展开放,对修改关闭
- 依赖抽象,不依赖具体类
设计要点
- 命令模式将发出请求的对象和执行请求的对象解耦
- 在被解耦的两者之间是通过命令对象进行沟通的;命令对象封装了一个或者一组的动作
- 调用者通过命令对象的 execute()发出请求,这会使得接受者的动作被调用
- 调用者可以接受命令当做参数,甚至可以在运行时动态进行
- 命令可以支持撤销功能,方法是通过实现一个 undo 方法来回到 execute 方法执行之前的状态
- 命令模式同时也支持使用宏命令,即无需修改源码来实现完成多个的功能的同时开关
实例设计
设计背景
实现一个多槽家用自动化遥控器,可以通过一个遥控器来实现控制起居室灯、花园洒水器,起居室吊扇、车库门、音响、所有的灯、舞会模式的开关,以及一些其他的拓展功能(能在不修改源码的前提下实现),同时实现一个撤销功能(撤销上一个实现的命令)以及宏命令功能。
设计想法
通过设置一个命令的统一规范接口来规范化各种命令
然后设置各自命令的具体实现类
适配到遥控器中(通过数组来实现多遥控适配)
项目类图
可以看出所有命令类都实现了接口 Command
项目结构
命令类规范化接口
设备实际操作类
设备命令实现类(体现命令模式的特点)
宏命令类以及空命令类
遥控器类(关键)
测试类
测试结果
---------------- Remote Control ---------------
[solt0] command.impl.allLight.AllLightOn command.impl.allLight.AllLightOff
[solt1] command.impl.ballPartern.BallParternOn command.impl.ballPartern.BallParternOff
[solt2] command.impl.garageDoor.GarageDoorOn command.impl.garageDoor.GarageDoorOff
[solt3] command.impl.gardenSprinkler.GardenSprinklerOn command.impl.garageDoor.GarageDoorOff
[solt4] command.impl.livingRoomFan.LivingRoomFanLow command.impl.livingRoomFan.LivingRoomFanOff
[solt5] command.impl.livingRoomFan.LivingRoomFanMedium command.impl.livingRoomFan.LivingRoomFanOff
[solt6] command.impl.livingRoomFan.LivingRoomFanHigh command.impl.livingRoomFan.LivingRoomFanOff
[solt7] command.impl.livingRoomLight.LivingRoomLightOn command.impl.livingRoomLight.LivingRoomLightOff
[solt8] command.impl.stereo.StereoOn command.impl.stereo.StereoOff
[solt9] command.impl.NullCommand command.impl.NullCommand
----------- All Light ---------------
All light is on!
All light is off!
All light is on!
----------- Garage Door ---------------
Garage Door is on!
Garage Door is off!
Garage Door is off!
----------- Fan ---------------
Living Room Fan is on low speed!
Living Room Fan is on medium speed!
Living Room Fan is on low speed!
---------------- Remote Control ---------------
[solt0] command.impl.allLight.AllLightOn command.impl.allLight.AllLightOff
[solt1] command.impl.ballPartern.BallParternOn command.impl.ballPartern.BallParternOff
[solt2] command.impl.garageDoor.GarageDoorOn command.impl.garageDoor.GarageDoorOff
[solt3] command.impl.gardenSprinkler.GardenSprinklerOn command.impl.garageDoor.GarageDoorOff
[solt4] command.impl.livingRoomFan.LivingRoomFanLow command.impl.livingRoomFan.LivingRoomFanOff
[solt5] command.impl.livingRoomFan.LivingRoomFanMedium command.impl.livingRoomFan.LivingRoomFanOff
[solt6] command.impl.livingRoomFan.LivingRoomFanHigh command.impl.livingRoomFan.LivingRoomFanOff
[solt7] command.impl.livingRoomLight.LivingRoomLightOn command.impl.livingRoomLight.LivingRoomLightOff
[solt8] command.impl.stereo.StereoOn command.impl.stereo.StereoOff
[solt9] command.impl.MacroCommand command.impl.MacroCommand
-------------- macroCommand --------------------
All light is on!
Garden Sprinkler is on!
Stereo is on!
Living Room light is on!
All light is off!
Garden Sprinkler is off!
Stereo is off!
Living Room light is off!
Process finished with exit code 0
分析
实现了通过把命令封装成类来实现的遥控器设置。
命令模式的现实用途
队列请求
想象有一个工作队列,我们在一端不断的添加命令,然后工作队列的线程只需要不断的从队列中取出命令并且调用命令的 execute()方法,等待这个调用完成,然后将命令对象丢弃掉,再继续循环下去。
那么在这个过程中,我们的工作队列和要进行运行的对象是完全解耦的,工作队列不需要知道运行的对象是什么方法,只需要运行 execute()方法就可以了,这一角度也反映了命令模式的特点,将工作执行者和调用者的解耦。
日志请求
日志系统需要我们实时记录下系统运行的动作,我们可以在 Command 接口中在规范新增两个方法 store()以及 load()方法。我们可以通过在每个命令进行的时候调用 store 方法保存动作到日志中,当遇到系统死机的情况,就将命令对象重新加载,调用 load 方法来获取进行的动作,并且成批的进行 execute 方法的调用以恢复到系统此前的状态。
事务系统
我们的命令模式也可以巧妙地利用在事务系统中,可以通过调用一整群的命令操作,并且要求要么全部一起完成,要么没有进行任何操作。
END
2019 年 9 月 7 日 16:57:03
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于