该系列文章系个人读书笔记及总结性内容,任何组织和个人不得转载进行商业活动!
命令模式:
封装调用——本节把封装带到了一个全新的境界:把方法调用(method invocation)封装起来;
通过封装方法调用,我们可以将功能模块包装成型,调用功能的模块不需要关心事情如何进行,只要知道如何使用包装成型的方法来完成功能即可;
家电自动化遥控器API示例:
七个可编程卡槽,对应七种家电的控制开关;
有一个整体的控制撤销的按钮;
针对每一种家电我们都有各自开发的JavaAPI来进行控制;
我们想要的是创建一组控制遥控器的API,让每个插槽对相应的电器进行控制,最主要的是可以支持未来可能出现的设备的控制;
我们可以在Machine插槽上接上不同的装置,然后既可以控制它了;
撤销按钮是整体公用的,会撤销最后一个按钮的动作;
不同的设备厂商为设备提供了接口类:
包括TV、Light等;而且这样的类还不少(很多家电),接口各异;
遥控器按下按钮执行动作,但不一定要知道具体家电实现方法的细节;
命令模式可以将“动作的请求者”从“动作的执行者”对象中解耦:
请求者是遥控器;
执行者是具体家电类的一个实例(一个电视机或是一盏灯);
设计中使用“命令对象”:
利用命令对象,把请求封装成一个特定对象;
当按下开关打开灯时,就可以请命令对象做相关的工作;遥控器并不知道具体做什么,只要命令对象能和正确的对象沟通即可;
这样,遥控器就和灯解耦了;
餐厅场景举例——命令模式的简单介绍:
顾客把订单给到招待,招待再把订单拿到柜台,并通知厨师订单来了,赶快准备,厨师根据订单制作菜品;
餐厅的角色和职责:
一张订单封装了准备点餐的请求;
订单对象可以被传递,且只有一个方法:orderUp();他封装了准备餐点的动作;
订单内有一个执行“准备餐点动作”的对象——厨师;
招待并不需要知道订单内容是什么,也不需要知道谁来准备餐点,他的工作是接受订单,然后调用订单的orderUp()方法;
这样,只要是顾客createOrder()的招待都可以takeOrder(),然后调用所有订单都需要有的orderUp();
招待不是让厨师准备餐点,而是调用订单的方法提醒厨师订单需要被准备了;即招待和厨师解耦了;
类似的场景对应一种设计模式——允许将“发出请求的对象”与“接受与执行请求的对象”分隔开;
招待发出准备餐点的请求——>【订单】——>厨师准备餐点
人操作家电的请求——>【遥控器】——>家电执行动作
遥控器不知道有哪些对象,但当按钮按下,调用该对象的orderUp()方法,灯就开了;
从餐厅到命令模式:
回到我们的遥控器——使用命令对象:
实现命令接口Command:所有命令对象实现相同的包含一个方法的接口;一般惯用名称execute();
命令的执行对象Light:如灯,可以打开或关闭;
实现具体的命令LightOnCommand:如开灯命令;
使用命令类SimpleControl: 假设一个只有一个按钮的遥控器;
log:
bogon:遥控器简单测试 huaqiang$ javac *.java
bogon:遥控器简单测试 huaqiang$ java SimpleControlDriver
Light on! For - LightA
此例中,遥控器就是调用者,会传入命令对象,可以用发请求;
定义命令模式:
命令模式将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象;
命令模式也支持可撤销的操作;
重点理解:
一个命令对象通过在特定接收者上绑定一组动作来来封装一个请求;
命令对象将接收者和动作包进对象中,只暴露一个execute()方法,此方法调用时,接收者就会执行这些动作;
对外,其他对象并不知道谁做了那些事,只知道,如果调用execute()方法,请求的目的就能达到;
命令模式类图:
客户构建命令;
让调用者(Invoker)持有请求(Command),用请求(ConcreteCommand)去通知命令的执行者(Receiver);
完成调用者和执行者之间的解耦;
完成7个插槽的遥控器:
类似简单遥控器所做的,我们需要提供一个方法,将命令指定到插槽;
实际上,我们将有7个插槽,每个插槽具备开关按钮;
1.实现遥控器
2.实现命令
3.测试遥控器
关键点:
NoCommand对象是一个空对象,是为了确定每个插槽永远都有命令;
我们看到execute()方法可以封装一组命令;
命令对象持有对一个厂商类的实例的引用,并实现一个execute()方法;
这个方法会调用厂商类实例的一个或多个方法,完成特定的行为;
加入撤销操作也很简单,只需要把execute之前执行的动作倒过来,如打开命令的撤销就是关闭;
宏命令-命令的“Party模式”:
比如在关灯的同时,关掉音响,关掉电视等;
我们可以通过一个MacroCommand来实现一个新命令,用来执行一组命令;
如果想实现多层次的撤销操作:
只需要使用一个堆栈记录操作的过程的每一个命令;不管什么时候按下了撤销按钮,都可以从堆栈中取出最上层的命令,然后调用他的undo()方法;
命令模式的更多用途:队列请求
命令可以将运算打包,一个接收者和一组动作;命令对象被调用之后,运算就可以被执行;
甚至是在不同的线程之中被调用;也衍生出一些应用:日程安排(Scheduler)、线程池、工作队列;
想象一个队列,你在某一端添加命令,然后另一端则是线程;
线程从队列中取出一个命令,调用它的execute()方法,等待这个调用完成,然后将此命令丢弃,再取出下一个命令;
这样能有效地把运算限制在固定数目的线程中进行;
不同的命令对象可以封装不同的功能,但只要实现了命令模式,就都可以加入队列中;
命令模式的更多请求:日志请求
某些应用需要将所有的动作都记录在日志中,并能在系统死机之后,调用这些动作恢复到之前的状态;
可以用命令模式支持,将历史命令存储到磁盘,需要时,重新加载,批量依次调用这些对象的execute()方法;
对于更高级的应用,这些技巧可以被扩展到事务处理中;
总结:
1.命令模式将发出请求的对象和执行请求的对象解构;
2.在被解耦的两者之间是通过命令对象进行沟通的,命令对象封装了接收者和一个或一组动作;
3.调用者通过调用命令对象的execute()发出请求,这会使得接收者的动作被调用;
4.调用者可以接受命令当做参数,甚至在运行时动态地进行;
5.命令可以支持撤销,做法是实现一个undo()方法来回倒execute()被执行前的状态;
6.宏命令是命令的一种简单延伸,允许调用多个命令;宏命令也支持撤销;
7.命令也可以用来实现日志和事务系统;
OO基础:
抽象;
封装
继承;
多态;
OO原则:
封装变化
多用组合,少用继承
针对接口编程,不针对实现编程
为交互对象之间的松耦合设计而努力;
类应该对扩展开放,对修改关闭;
依赖抽象,不要依赖具体类;
OO模式:
策略模式:定义算法族,分别封装起来,让他们之间互相替换,此模式让算法的变化独立于使用算法的客户;
观察者模式:在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会收到通知,并自动更新;
装饰者模式:动态地将责任附加到对象上;想要扩展功能,装饰者提供有别于继承的另一种选择;
简单工厂模式;
工厂方法模式:定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个;工厂方法让类把实例化推迟到子类;
抽象工厂模式:提供一个接口,用于创建相关或依赖对象的家族,而不需要明确具体的类;
单件模式:确保一个类只有一个实例,并提供全局访问点;
——命令模式:将请求封装成对象,这可以让你使用不同的请求,队列或者日志请求来参数化其他对象;命令模式也支持撤销操作;