命令模式

什么是命令模式

将请求封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。

命令模式特点

1.命令模式将发出请求的对象和执行请求的对象解耦
2.被解耦的两者之间是通过命令对象进行沟通的。命令对象封装了接受者的一个或一组动作
3.调用者通过调用命令对象的execute()发出请求,这会使得接受者的动作被调用
4.调用者可以接受命令当做参数,甚至在运行时动态地进行。
5.命令可以支持撤销,做法是实现一个undo()方法来回到execute()被执行前的状态
6.宏命令是一种简单的延伸,允许调用多个命令,宏方法也可以支持撤销
7.实际操作时,很常见使用“聪明”命令对象,也就是直接实现了请求,而不是将工作直接委托给接收者
8.命令也可以用来实现日志和事物系统

一个使用了命令模式设计的程序

首先我们有一个命令接口,所有的命令都必须实现该接口

1
2
3
4
5
6
7
/**
* 命令接口
*/
public interface Command {
public void execute();
public void undo();
}

灯光类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 灯光类
*/
public class Light {
String location;
public Light(String location){
this.location = location;
}
public void on(){
System.out.println(location+"灯亮了");
}
public void off(){
System.out.println(location+"灯灭了");
}
}

控制灯光开

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 控制灯光开
*/
public class LightOnCommand implements Command {
Light light;
public LightOnCommand(Light light){
this.light = light;
}
@Override
public void execute() {
light.on();
}
@Override
public void undo() {
light.off();
}
}

控制灯光关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 控制灯光关
*/
public class LightOffCommand implements Command {
Light light;
public LightOffCommand(Light light){
this.light = light;
}
@Override
public void execute() {
light.off();
}
@Override
public void undo() {
light.on();
}
}

音响类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* 音响类
*/
public class Stereo {
String location;
public Stereo(String location){
this.location = location;
}
public void on(){
System.out.println("打开音响");
}
public void setCD(){
System.out.println("放入一张CD");
}
public void setDvd(){
System.out.println("Dvd模式");
}
public void setRadio(){
System.out.println("Radio模式");
}
public void setVolume(int volume){
System.out.println("声音设置为["+volume+"]分贝");
}
public void off(){
System.out.println("关闭音响");
}
}

控制音响开

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 音响开命令
*/
public class StereoOnWithCDCommand implements Command {
Stereo stereo;
public StereoOnWithCDCommand(Stereo stereo){
this.stereo = stereo;
}
@Override
public void execute() {
stereo.on();
stereo.setCD();
stereo.setVolume(11);
}
@Override
public void undo() {
stereo.off();
}
}

控制音响关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 音响关命令
*/
public class StereoOffWithCDCommand implements Command {
Stereo stereo;
public StereoOffWithCDCommand(Stereo stereo){
this.stereo = stereo;
}
@Override
public void execute() {
stereo.off();
}
@Override
public void undo() {
stereo.on();
stereo.setCD();
stereo.setVolume(11);
}
}

接下来还有一个风扇类,风扇有高中低三挡之分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
* 吊扇类
*/
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(){
speed = HIGH;
System.out.println(location+"吊扇以高速运行");
}
public void medium(){
speed = MEDIUM;
System.out.println(location+"吊扇以中速运行");
}
public void low(){
speed = LOW;
System.out.println(location+"吊扇以低速运行");
}
public void off(){
speed = OFF;
System.out.println(location+"吊扇关闭");
}
public int getSpeed(){
return speed;
}
}

风扇高档命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* 吊扇高速
*/
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();
}
}
}

风扇中档命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* 吊扇中速
*/
public class CeilingFanMediumCommand implements Command {
CeilingFan ceilingFan;
int prevSpeed;
public CeilingFanMediumCommand(CeilingFan ceilingFan) {
this.ceilingFan = ceilingFan;
}
@Override
public void execute() {
prevSpeed = ceilingFan.getSpeed();
ceilingFan.medium();
}
@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();
}
}
}

风扇低档命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* 吊扇低速
*/
public class CeilingFanLowCommand implements Command {
CeilingFan ceilingFan;
int prevSpeed;
public CeilingFanLowCommand(CeilingFan ceilingFan) {
this.ceilingFan = ceilingFan;
}
@Override
public void execute() {
prevSpeed = ceilingFan.getSpeed();
ceilingFan.low();
}
@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();
}
}
}

吊扇关命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* 吊扇关命令
*/
public class CeilingFanOffCommand implements Command {
CeilingFan ceilingFan;
int prevSpeed;
public CeilingFanOffCommand(CeilingFan ceilingFan){
this.ceilingFan = ceilingFan;
}
@Override
public void execute() {
prevSpeed = ceilingFan.getSpeed();
ceilingFan.off();
}
@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();
}
}
}

无命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 无命令
*/
public class NoCommand implements Command {
@Override
public void execute() {
System.out.println("无命令");
}
@Override
public void undo() {
System.out.println("无命令undo");
}
}

party命令组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* party模式命令
*/
public class MacroCommand implements Command {
Command[] commands;
public MacroCommand(Command[] commands){
this.commands = commands;
}
@Override
public void execute() {
for(int i = 0,size = commands.length; i < size; i++){
commands[i].execute();
}
}
@Override
public void undo() {
for(int i = 0,size = commands.length; i < size; i++){
commands[i].undo();
}
}
}

远程控制类(遥控器)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/**
* 远程控制类
*/
public class RemoteControl {
Command[] onCommands;
Command[] offCommands;
Command undoCommand;
/**
* 初始化命令数组
*/
public RemoteControl(){
onCommands = new Command[7];
offCommands = new Command[7];
Command noCommand = new NoCommand();
for(int i = 0; i < 7; i++){
onCommands[i] = noCommand;
offCommands[i] = noCommand;
}
undoCommand = noCommand;
}
/**
* 设置命令
* @param slot 命令在遥控器上的位置(总共有七行命令)
* @param onCommand
* @param offCommand
*/
public void setCommand(int slot,Command onCommand,Command offCommand){
onCommands[slot] = onCommand;
offCommands[slot] = offCommand;
}
/**
* 开的命令
* @param slot
*/
public void onButtonWasPushed(int slot){
onCommands[slot].execute();
undoCommand = onCommands[slot];
}
/**
* 关的命令
* @param slot
*/
public void offButtonWasPushed(int slot){
offCommands[slot].execute();
undoCommand = offCommands[slot];
}
/**
* 撤销命令
*/
public void undoButtonWasPushed(){
undoCommand.undo();
}
public String toString(){
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("\n----- Remote Control -----\n");
for(int i = 0,len = onCommands.length; i < len ; i++){
stringBuffer.append("[slot "+i+"] "+onCommands[i].getClass().getName()+" "+offCommands[i].getClass().getName()+"\n");
}
return stringBuffer.toString();
}
}

再来一个测试类测试一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
public class Test {
public static void main(String[] args){
RemoteControl remoteControl = new RemoteControl();
Light livingRoomLight = new Light("Living Room");
Light kitchenLight = new Light("Kitchen");
CeilingFan ceilingFan = new CeilingFan("Living Room");
Stereo stereo = new Stereo("Living Room");
LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight);
LightOffCommand livingRoomLightOff = new LightOffCommand(livingRoomLight);
LightOnCommand kitchenLightOn = new LightOnCommand(kitchenLight);
LightOffCommand kitchenLightOff = new LightOffCommand(kitchenLight);
CeilingFanLowCommand ceilingFanLowCommand = new CeilingFanLowCommand(ceilingFan);
CeilingFanOffCommand ceilingFanOffCommand = new CeilingFanOffCommand(ceilingFan);
CeilingFanHighCommand ceilingFanHighCommand = new CeilingFanHighCommand(ceilingFan);
CeilingFanMediumCommand ceilingFanMediumCommand = new CeilingFanMediumCommand(ceilingFan);
StereoOnWithCDCommand stereoOnWithCDCommand = new StereoOnWithCDCommand(stereo);
StereoOffWithCDCommand stereoOffWithCDCommand = new StereoOffWithCDCommand(stereo);
remoteControl.setCommand(0,livingRoomLightOn,livingRoomLightOff);
remoteControl.setCommand(1,kitchenLightOn,kitchenLightOff);
remoteControl.setCommand(2,ceilingFanLowCommand,ceilingFanOffCommand);
remoteControl.setCommand(3,stereoOnWithCDCommand,stereoOffWithCDCommand);
System.out.println(remoteControl);
remoteControl.onButtonWasPushed(0);
remoteControl.offButtonWasPushed(0);
remoteControl.onButtonWasPushed(1);
remoteControl.offButtonWasPushed(1);
remoteControl.onButtonWasPushed(2);
remoteControl.offButtonWasPushed(2);
remoteControl.onButtonWasPushed(3);
remoteControl.offButtonWasPushed(3);
System.out.println("这里是分割线------------");
remoteControl.onButtonWasPushed(0);
remoteControl.offButtonWasPushed(0);
remoteControl.undoButtonWasPushed();
remoteControl.offButtonWasPushed(0);
remoteControl.onButtonWasPushed(0);
remoteControl.undoButtonWasPushed();
System.out.println("这里是分割线------------");
remoteControl.setCommand(4,ceilingFanHighCommand,ceilingFanOffCommand);
remoteControl.setCommand(5,ceilingFanMediumCommand,ceilingFanOffCommand);
System.out.println(remoteControl);
remoteControl.onButtonWasPushed(4);
remoteControl.offButtonWasPushed(4);
remoteControl.undoButtonWasPushed();
remoteControl.onButtonWasPushed(5);
remoteControl.undoButtonWasPushed();
System.out.println("这里是分割线-------准备执行party模式-----");
Command[] partyOn = {livingRoomLightOn,stereoOnWithCDCommand,ceilingFanHighCommand};
Command[] partyOff = {livingRoomLightOff,stereoOffWithCDCommand,ceilingFanOffCommand};
MacroCommand macroCommandOn = new MacroCommand(partyOn);
MacroCommand macroCommandOff = new MacroCommand(partyOff);
remoteControl.setCommand(6,macroCommandOn,macroCommandOff);
remoteControl.onButtonWasPushed(6);
remoteControl.offButtonWasPushed(6);
remoteControl.undoButtonWasPushed();
}
}

输出结果如下

—– Remote Control —–
[slot 0] com.headfirst.command.light.LightOnCommand com.headfirst.command.light.LightOffCommand
[slot 1] com.headfirst.command.light.LightOnCommand com.headfirst.command.light.LightOffCommand
[slot 2] com.headfirst.command.fan.CeilingFanLowCommand com.headfirst.command.fan.CeilingFanOffCommand
[slot 3] com.headfirst.command.stereo.StereoOnWithCDCommand com.headfirst.command.stereo.StereoOffWithCDCommand
[slot 4] com.headfirst.command.NoCommand com.headfirst.command.NoCommand
[slot 5] com.headfirst.command.NoCommand com.headfirst.command.NoCommand
[slot 6] com.headfirst.command.NoCommand com.headfirst.command.NoCommand

Living Room灯亮了
Living Room灯灭了
Kitchen灯亮了
Kitchen灯灭了
Living Room吊扇以低速运行
Living Room吊扇关闭
打开音响
放入一张CD
声音设置为[11]分贝
关闭音响
这里是分割线————
Living Room灯亮了
Living Room灯灭了
Living Room灯亮了
Living Room灯灭了
Living Room灯亮了
Living Room灯灭了
这里是分割线————

—– Remote Control —–
[slot 0] com.headfirst.command.light.LightOnCommand com.headfirst.command.light.LightOffCommand
[slot 1] com.headfirst.command.light.LightOnCommand com.headfirst.command.light.LightOffCommand
[slot 2] com.headfirst.command.fan.CeilingFanLowCommand com.headfirst.command.fan.CeilingFanOffCommand
[slot 3] com.headfirst.command.stereo.StereoOnWithCDCommand com.headfirst.command.stereo.StereoOffWithCDCommand
[slot 4] com.headfirst.command.fan.CeilingFanHighCommand com.headfirst.command.fan.CeilingFanOffCommand
[slot 5] com.headfirst.command.fan.CeilingFanMediumCommand com.headfirst.command.fan.CeilingFanOffCommand
[slot 6] com.headfirst.command.NoCommand com.headfirst.command.NoCommand

Living Room吊扇以高速运行
Living Room吊扇关闭
Living Room吊扇以高速运行
Living Room吊扇以中速运行
Living Room吊扇以高速运行
这里是分割线——-准备执行party模式—–
Living Room灯亮了
打开音响
放入一张CD
声音设置为[11]分贝
Living Room吊扇以高速运行
Living Room灯灭了
关闭音响
Living Room吊扇关闭
Living Room灯亮了
打开音响
放入一张CD
声音设置为[11]分贝
Living Room吊扇以高速运行

总结

当需要把请求对象和和执行请求对象解耦的时候,我们就使用命令模式。