网站建设报价单ppt,wordpress多选展示表单,天元建设集团有限公司张桂玉丑闻事件,怎么做公司网站的二维码文章目录 定义图纸一个例子#xff1a;空调和他的遥控器只有控制面板的空调遥控器可以撤销的操作 碎碎念命令和Runnable命令和事务 定义
把请求封装成一个对象#xff0c;从而使你可以用不同的请求对客户进行参数化#xff0c;对请求排队或记录请求日志#xff0c;以及支持… 文章目录 定义图纸一个例子空调和他的遥控器只有控制面板的空调遥控器可以撤销的操作 碎碎念命令和Runnable命令和事务 定义
把请求封装成一个对象从而使你可以用不同的请求对客户进行参数化对请求排队或记录请求日志以及支持可撤销的操作 在职责链中我们把不同的动作分支组合在一起让请求在不同的分支中进行流通他可以是逻辑上的流通也可以是封装成一个参数对象在里面流通。而在命令模式中这种思想进一步升级他会不分青红皂白的把所有请求封装成命令对象 图纸 各位道友应该知道有个叫枪的东西吧一种装上子弹就可以开火的设备 和由枪和子弹构成的模式一样命令模式是由 实际执行操作的【Receiver】 和 命令对象【ConcreteCommand】 构成如果把 Receiver 看成 枪那么 ConcreteCommand 就是 子弹 每次要开枪执行指令之前你都要获取子弹获取对应的 ConcreteCommand 对象上膛组合 Receiver 和 ConcreteCommand 对象最后扣动扳机调用 Receiver 的 Action 方法 一个例子空调和他的遥控器
假定你入职了一家空调公司接手了和立式空调有关的模块。不久之后公司决定给这个立式空调增加一个遥控器原有的立式空调全部都是通过控制面板上的按钮进行操作的。正当你摩拳擦掌准备大干一番的时候前辈留给你的紧耦合代码却让你差点当场骂娘。困难面前你是要说服上司放弃遥控器的企划还是连夜删库跑路又或者重构已有的代码……
铺垫有点太长了总之这次的例子开始了 只有控制面板的空调
言归正传前辈留下的代码是这样的 AirConditioner空调
/*** 空调*/
public class AirConditioner {public static final int MAX_TEMPERATURE 32;//最高温度public static final int MIN_TEMPERATURE 18;//最低温度private static final String[] MODE_ARRAY {制冷模式, 睡眠模式, 送风模式};/*** 温度*/private Float temperature;/*** 当前模式*/private Integer modePoint;/*** 是否是开启的*/private boolean isOn;/*** 控制面板*/private ControlPanel controlPanel;public AirConditioner() {controlPanel ControlPanel.createControlPanel(this);}public static String[] getModeArray() {return MODE_ARRAY.clone();}public Float getTemperature() {return temperature;}public void setTemperature(Float temperature) {if (temperature null || (temperature MIN_TEMPERATURE temperature MAX_TEMPERATURE)) {this.temperature temperature;}}public String getMode() {return MODE_ARRAY[modePoint];}public Integer getModePoint() {return modePoint;}public void setModePoint(Integer modePoint) {this.modePoint modePoint;}public boolean isOn() {return isOn;}public void setOn(boolean on) {isOn on;}
}ControlPanel
/*** 控制面板*/
public class ControlPanel {private final AirConditioner airConditioner;private ControlPanel(AirConditioner airConditioner) {this.airConditioner airConditioner;}public static ControlPanel createControlPanel(AirConditioner airConditioner) {ControlPanel controlPanel new ControlPanel(airConditioner);controlPanel.off();return controlPanel;}/*** 开机*/public void on() {if (!airConditioner.isOn()) {//关机模式才可以执行这个动作airConditioner.setOn(true);//设定开机airConditioner.setTemperature(26f);//默认26度airConditioner.setModePoint(0);//默认第一个模式}}/*** 关机*/public void off() {if (airConditioner.isOn()) {//开机状态才可以执行这个动作airConditioner.setOn(false);airConditioner.setTemperature(null);airConditioner.setModePoint(null);}}/*** 温度上升1*/public void addTemperature() {if (airConditioner.isOn()) {airConditioner.setTemperature(airConditioner.getTemperature() 1);}}/*** 温度下降1*/public void lessenTemperature() {if (airConditioner.isOn()) {airConditioner.setTemperature(airConditioner.getTemperature() - 1);}}/*** 下一模式*/public void nextMode() {Integer modePoint airConditioner.getModePoint();if (airConditioner.isOn()) {if (modePoint 1 AirConditioner.getModeArray().length) {airConditioner.setModePoint(modePoint 1);} else {airConditioner.setModePoint(0);}}}
}在这个只有控制面板的立式空调里面前辈通过 AirConditioner空调 来表示一部空调的底层函数然后通过 ControlPanel控制面板 为 Client 提供操作空调内部属性的接口
虽然它可以如预期一般完成任务但这种设计绝算不上优雅他的问题主要体现在 控制面板 和空调底层方法之间的耦合过于紧密现在只有一种类型的空调如果有底层方法不相同的第二种类型的空调出现那么这个 控制面板 是无法与其兼容的 遥控器
遥控器 的引入改变了现态很显然遥控器 至少需要拥有和 控制面板 一样的效果而且一个 遥控器 必须可以同时对应多个 空调 对象。也就是说你只有在最终调用遥控器上面的任务的时候才会知道到底要调用哪个空调上的方法做出来的效果应该是这样的 我们新增了 RemoteController遥控器 作为遥控器的实现而 RemoteController 和 ControlPanel 的实现唯一的区别就在于 ControlPanel 的 AirConditioner 从他诞生的时候就被定义且无法修改而 RemoteController 的 AirConditioner 在调用命令的时候才会被指定
除此之外程序中出现了大量的重复代码这些重复代码分布在 控制面板 和 遥控器 的每一个对应方法中
有没有办法把他们解耦我的意思是把调用者和他的实现解耦也就是说做成这样的效果 放在本例中他长这样 Executor Pool
/*** 空调命令执行器*/
public abstract class ACExecutor implements Cloneable {/*** 执行命令*/public abstract void execute(AirConditioner airConditioner);/*** 克隆方法覆盖Object中的clone*/public ACExecutor clone() {try {return (ACExecutor) super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();throw new RuntimeException(e);//异常原样抛出}}
}public class OnExecutor extends ACExecutor {Overridepublic void execute(AirConditioner airConditioner) {if (!airConditioner.isOn()) {//关机模式才可以执行这个动作airConditioner.setOn(true);//设定开机airConditioner.setTemperature(26f);//默认26度airConditioner.setModePoint(0);//默认第一个模式}}
}public class OffExecutor extends ACExecutor {Overridepublic void execute(AirConditioner airConditioner) {if (airConditioner.isOn()) {//开机状态才可以执行这个动作airConditioner.setOn(false);airConditioner.setTemperature(null);airConditioner.setModePoint(null);}}
}public class AddTemperatureExecutor extends ACExecutor {Overridepublic void execute(AirConditioner airConditioner) {if (airConditioner.isOn()) {airConditioner.setTemperature(airConditioner.getTemperature() 1);}}
}public class LessenTemperatureExecutor extends ACExecutor {Overridepublic void execute(AirConditioner airConditioner) {if (airConditioner.isOn()) {airConditioner.setTemperature(airConditioner.getTemperature() - 1);}}
}public class NextModeExecutor extends ACExecutor {Overridepublic void execute(AirConditioner airConditioner) {Integer modePoint airConditioner.getModePoint();if (airConditioner.isOn()) {if (modePoint 1 AirConditioner.getModeArray().length) {airConditioner.setModePoint(modePoint 1);} else {airConditioner.setModePoint(0);}}}
}/*** 空调命令执行器的对象池*/
public class ACExecutorPool {//单例相关private static final ACExecutorPool INSTANCE new ACExecutorPool();public static ACExecutorPool getInstance() {return INSTANCE;}//原型池private final MapClass? extends ACExecutor, ACExecutor prototypePool new HashMap();public ACExecutor createOnExecutor() {return getNewObjectByPool(OnExecutor.class);}public ACExecutor createOffExecutor() {return getNewObjectByPool(OffExecutor.class);}public ACExecutor createAddTemperatureExecutor() {return getNewObjectByPool(AddTemperatureExecutor.class);}public ACExecutor createLessenTemperatureExecutor() {return getNewObjectByPool(LessenTemperatureExecutor.class);}public ACExecutor createNextModelExecutor() {return getNewObjectByPool(NextModeExecutor.class);}/*** 从原型池中获取对应的新命令对象*/private ACExecutor getNewObjectByPool(Class? extends ACExecutor c) {if (prototypePool.containsKey(c)) {return prototypePool.get(c).clone();} else {try {ACExecutor prototype c.newInstance();prototypePool.put(c, prototype);return prototype.clone();} catch (InstantiationException | IllegalAccessException e) {e.printStackTrace();throw new RuntimeException(初始化命令对象原型池的时候出现了异常具体异常为: e.getCause(), e);}}}
}ControlPanel RemoteController
/*** 控制面板*/
public class ControlPanel {private final AirConditioner airConditioner;private ControlPanel(AirConditioner airConditioner) {this.airConditioner airConditioner;}public static ControlPanel createControlPanel(AirConditioner airConditioner) {ControlPanel controlPanel new ControlPanel(airConditioner);controlPanel.off();return controlPanel;}/*** 开机*/public void on() {ACExecutorPool.getInstance().createOnExecutor().execute(airConditioner);}/*** 关机*/public void off() {ACExecutorPool.getInstance().createOffExecutor().execute(airConditioner);}/*** 温度上升1*/public void addTemperature() {ACExecutorPool.getInstance().createAddTemperatureExecutor().execute(airConditioner);}/*** 温度下降1*/public void lessenTemperature() {ACExecutorPool.getInstance().createLessenTemperatureExecutor().execute(airConditioner);}/*** 下一模式*/public void nextMode() {ACExecutorPool.getInstance().createNextModelExecutor().execute(airConditioner);}
}/*** 遥控器*/
public class RemoteController {/*** 开机*/public void on(AirConditioner airConditioner) {ACExecutorPool.getInstance().createOnExecutor().execute(airConditioner);}/*** 关机*/public void off(AirConditioner airConditioner) {ACExecutorPool.getInstance().createOffExecutor().execute(airConditioner);}/*** 温度上升1*/public void addTemperature(AirConditioner airConditioner) {ACExecutorPool.getInstance().createAddTemperatureExecutor().execute(airConditioner);}/*** 温度下降1*/public void lessenTemperature(AirConditioner airConditioner) {ACExecutorPool.getInstance().createLessenTemperatureExecutor().execute(airConditioner);}/*** 下一模式*/public void nextMode(AirConditioner airConditioner) {ACExecutorPool.getInstance().createNextModelExecutor().execute(airConditioner);}
}在这种实现方式里我们把具体执行的操作封装到 ACExecutor空调执行器 类簇中为 所有的操作定义了自己的执行类然后在 client 点击 控制面板 和 遥控器 上的对应按钮的时候把让他们获取对应的 执行器对象并执行对应的操作。而 控制面板 和 遥控器 根本不关心 执行器 的底层做了什么操作这让他们从命令的执行者变成了命令的调度者 听起来很复杂但是这种复杂是 颗粒度 细致程度导致的五个命令导致我们需要五个对应的执行者子类流程上其实是很简单的。以开机为例我们的程序其实是这样做的 这种让程序变得复杂的写法是有价值的至少可以让你在以下两种情况可以少写很多代码
当出现新的操作面板比如手机APP控制空调的时候我就可以通过创建新的控制器类并让他调用已有的执行者来实现而不需要再重复执行者里面的代码如果出现了接口一致但行为不一致的空调那我依然可以继续使用控制面板和遥控器只需要建立对应的执行器类簇即可
而这正是一个标准的命令实现 可以撤销的操作
一般我讲到 ”这正是xxx的实现“ 的时候例子就结束了但这次是例外
请留意一下上例的一个配角类—— ACExecutorPool这个类的作用是为我们的程序产出可靠的执行类对象。在上例中这一个类里用到了两种模式分别是 单例 和 原型
单例很好理解为了让全局都用一个对象池
为什么要用原型呢每个执行器都用单例不是更节省吗 这种情况下会用原型只有一种可能那就是执行器应该是带状态的而且他的状态是有意义的
那你会说了不对啊上例的执行器哪有状态。别急业务来了 某日接到通知我们需要在 控制面板 上添加一个 返回back 按钮用于撤销我们刚刚执行的命令要怎么实现呢
如果你没有使用命令模式实现这种功能费死劲但是在命令模式的框架下你可以这样做 我们在 ACExecutor 中提供了用于回滚的方法 back而在 控制面板 中我们通过添加 history 列表的方式存储已经执行过的执行器以便我们回滚
这时候 ACExecutor 的对象就绝不能用单例了因为他的属性是一种凭证用于证明这个执行器对象有没有执行成功。此时原型模式就是你比较合适的选择了因为执行器对象是会被大量创建的原型可以有效的降低创建执行器的开销复制初始属性
而这也是命令模式所能实现的功能之一 碎碎念
命令和Runnable
Java的多线程模块中用到了很标准的命令模式
我们通过 Thread 来管控线程但是线程具体如何执行是由 Runnable 来决定的
也就是说 Thread 本质上其实就是 ReceiverRunnable 才是掌握具体内容的执行器 命令和事务
命令模式中的执行器的颗粒度你是可以自己掌握的你可以只让他执行单体命令也可以让他执行多个指令捆绑在一起的复合指令
这就是SQL中的事务一样他是一个具有 原子性 的整体一荣俱荣一损俱损 万分感谢您看完这篇文章如果您喜欢这篇文章欢迎点赞、收藏。还可以通过专栏查看更多与【设计模式】有关的内容