0. 序
不知道怎么写序了 遇到了一个之前未遇到的模式。
这一章看到尾确实是太僵硬了,后面会附上我在这章的练习代码!
1. 命令模式
命令模式将“请求”封装成对象,以便使用不同的请求,队列或者日志来参数化其他对象,命令模式也支持可撤销的操作。
命令模式可将“动作请求者”从“动作执行者”对象中解耦。例如:按下按钮后,遥控器就要把电灯打开,那么在此遥控器的实现中就有电灯对象的直接参与造成耦合。–> 如何解耦–> 把请求封装(如打开电灯)成一个特定对象,那么,如果该按钮存储一个命令对象,那么当按钮按下后,就可以命令对象做相应的工作,而不需要知道工作内容是什么。
例子:餐厅中客人点餐,服务员拿了订单,服务员把订单放到窗口,厨师根据订单准备餐点。在这里订单即封装了点餐请求(发出命令),服务员传递订单到窗口(命令传递),这里服务员不需要知道订单上有什么,已经订单给谁。厨师准备餐点(命令实现)。
空对象的使用 指的是不干任何事的对象,可用避免在使用该种类对象时判断if(command!=null) command.execute();
使用空对象只管调用execute()即可,如果为空的话它什么也不会做(p214)
class OneCommand implements Commend{
execute(){
//执行方法语句
....
}
}
class NoCommand implements Commend{
execute(){
//这里是空的
}
}
命令模式的更多用途:日程安排(Scheduler) 线程池 工作队列,日志请求等
2. Exercise Code
个人因为这一章的模式之前未接触过,在看书的过程中有似懂非懂的感觉 过完这一章后来梳理一下代码
例子:遥控器的实现
public class Light {
public void on(){
System.out.println("灯亮了");
};
public void off(){
System.out.println("灯灭了");
};
}
//先不使用命令模式 对一个遥控器该如何实现
/**
* 假设遥控器有三个槽
* 每个槽对应开 关 两个按钮 on off
*/
public class 遥控器 {
private String slot1;//槽1
private String slot2;//槽1
private String slot3;//槽1
public void slot1OnExecute(){
if (slot1==Light){
Light.on();
}else if (slot1==Door){
Door.on();
}....
}
...
}
//很显然 这里会有很多if-else 并且如果新增加一个槽的功能,就要加一个if-else 不好维护
因此,采用命令模式,将遥控器的指令封装起来–Command, 该封装带有一个执行方法execute(), 在获取Command对象后执行execute()即可完成命令
/**
* 封装命令的对象
*/
public interface Command {
void execute();//执行方法
}
/**
* 灯亮
*/
public class LightOnCommand implements Command{
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
}
/**
* 遥控器
*/
public class RemoteControl {
Command slot;//一个槽对应一个命令的执行 这里以一个槽为例
public RemoteControl() {
}
public void setSlot(Command slot) {
//设置每个槽对应的指令
this.slot = slot;
}
public void button1_1Pressed(){
//槽被按下 执行该槽的方法
slot.execute();
}
}
//测试
public static void main(String[] args) {
RemoteControl re=new RemoteControl();
Light light =new Light();
LightOnCommand lightOnCommand=new LightOnCommand(light);
re.setSlot(lightOnCommand);
re.button1_1Pressed();
}
实现多个槽的遥控器 并带有撤销命令 关于撤销操作,书中的想法很简单,拿出一个变量来记录上一次的命令,若撤销按下则执行该变量记录的命令的undo方法,这里我在想把undo也可撤销,如果要实现这一功能的话就需要记录下undo对应的功能和槽号了。
进阶的撤销功能,状态撤销–电风扇的“低,中,高 ”三档
/**
* 风扇类
*/
public class CeilingFan {
public static final int HIGH=3;
public static final int MEDIUM=2;
public static final int LOW=1;
public static final int OFF=0;
String location;
int speed; //记录当前的速度状态
public CeilingFan(String location){
this.location=location;
speed=OFF;
}
public void high(){
System.out.println("当前执行:"+ location+ " HIGH");
speed=HIGH;
}
public void medium(){
System.out.println("当前执行:"+ location+ " MEDIUM");
speed=MEDIUM;
}
public void low(){
System.out.println("当前执行:"+ location+ " LOW");
speed=LOW;
}
public void off(){
System.out.println("当前执行:"+ location+ " OFF");
speed=OFF;
}
public int getSpeed() {
return speed;
}
}
//高命令 中 低 关闭命令类似
public class CeilingFanHighCommand implements Command {
CeilingFan ceilingFan;
int prevSpeed;//之前的速度 作为撤销命令使用
public CeilingFanHighCommand(CeilingFan ceilingFan){
this.ceilingFan=ceilingFan;
}
@Override
public void execute() {
prevSpeed=ceilingFan.getSpeed();
ceilingFan.high();
}
@Override
public void undo() {
if (prevSpeed==CeilingFan.HIGH)
ceilingFan.high();
else if (prevSpeed==CeilingFan.MEDIUM)
ceilingFan.medium();
else if (prevSpeed==CeilingFan.LOW)
ceilingFan.low();
else if (prevSpeed==CeilingFan.OFF)
ceilingFan.off();
}
}
//测试
CeilingFan ceilingFan_room=new CeilingFan("房间");
CeilingFanHighCommand ceilingFanHighCommand=new CeilingFanHighCommand(ceilingFan_room);
CeliFanMediumCommand celiFanMediumCommand=new CeliFanMediumCommand(ceilingFan_room);
CeliFanLowCommand celiFanLowCommand=new CeliFanLowCommand(ceilingFan_room);
CeliFanOffCommand celiFanOffCommand=new CeliFanOffCommand(ceilingFan_room);
advancedRemoteControl.setCommand(4,ceilingFanHighCommand,celiFanOffCommand);
advancedRemoteControl.setCommand(5,celiFanMediumCommand,celiFanOffCommand);
advancedRemoteControl.setCommand(6,celiFanLowCommand,celiFanOffCommand);
advancedRemoteControl.hasOnButtonWasPressed(4);//高
advancedRemoteControl.hasOnButtonWasPressed(5);//中
advancedRemoteControl.undoButtonWasPressed();
当前执行:房间 HIGH
当前执行:房间 MEDIUM
当前执行:房间 HIGH
3. 扩展
一趟走下来发现命令模式其实就是 把一堆的指令封装起来(封装成command) 调用者只需要 对command执行execute()即可,不需要关心该命令如何实现的,不关心该命令的接收者,实现解耦。 即 风扇是一个接收者,打开/调速风扇是指令,将这种指令封装成命令(command) ,在命令封装的过程中就已经确定了该命令的接收者(构造器构造), 再然后在遥控器(调用者)上设置按钮对应的命令,只要按钮被按下就执行execute()方法。
命令模式更多用途:队列请求-- 命令将运算块打包(运算快内包含指令和接收者),然后将他传来传去,直到调用。那么“日程安排(scheduler),线程池,工作队列”等都是这个原理。
这种情况下,工作队列中的类与进行运算的线程之间是完全解耦的,线程只需要执行队列中的execute()方法即可,而不必关系这个对象是去读取网络数据还是进行数学计算。