介绍
命令模式(Command Pattern),将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化。命令模式可将“动作请求者”从“动作执行者”对象中解耦。
网络上面的文章,大部分都是对它这么定义的。说实话很抽象,理解难度较大。
不过你换个角度去想一下,就会比较好理解。如果你使用过Linux,并且使用过它的命令,就会发现很将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化
这一句话的意思了。
为什么呢?很简单,举个例子:比如在Linux中,我想解压一个tar压缩包,使用命令tar -cf ncov.tar ncov
就可以完成。但后来,我又想把显示压缩的一些文件列表,于是我又加上了一个参数-v
,于是它就成了这样:tar -cvf ncov.tar ncov
。
按照,上面举的一个例子,只需要仔细想一下,其实很容易就能理解命令模式。
由于命令模式属于不是很常用的设计模式,在学习它的过程中,想到接触过的项目中,暂时还没有一个合适的业务场景去使用这种设计模式,那就先按照《Head Frist 设计模式》中的一些案例来进行理解。
书中案例
现在有一种遥控器,遥控器很多种按钮,每种按钮都有不同的功能。利用命令对象,把请求(例如打开电灯)封装成一个让特定的对象(例如客厅电灯对象)。所以,如果每个按钮都存储一个命令对象,那么当按钮按下的时候,就可以请命令对象做相关的工作。
实现命令接口:
public interface Command {
void execute();
}
实现一个打开电灯的命令
现在,假设想实现一个打开电灯的命令。根据厂商所提供的类,Light有两个方法:on()和off()。
public class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
}
使用命令对象
既然是遥控器,那就需要一个遥控器的对象:
public class SimpleRemoteControl {
private Command slot;
public void setSlot(Command slot) {
this.slot = slot;
}
public void buttonWasPressed() {
slot.execute();
}
}
测试使用
遥控器和开关命令都实现了,那么现在就来一个测试类测试一下:
public class RemoteControlTest {
public static void main(String[] args) {
SimpleRemoteControl simpleRemoteControl = new SimpleRemoteControl();
Light light = new Light();
LightOnCommand lightOnCommand = new LightOnCommand(light);
simpleRemoteControl.setSlot(lightOnCommand);
simpleRemoteControl.buttonWasPressed();
}
}
现在我们实现了一个简单遥控器,但大部分遥控器都有很多按钮,不同的按钮对应着不同的操作。
那么我们现在来实现一个有多个按钮的遥控器:
public class RemoteControl {
private final int slotSize = 2;
private final Command[] onCommands;
private final Command[] offCommands;
public RemoteControl() {
onCommands = new Command[slotSize];
offCommands = new Command[slotSize];
// 初始化时的按钮都没有任何命令
Command noCommand = new NoCommand();
for (int i = 0; i < slotSize; i++) {
onCommands[i] = noCommand;
offCommands[i] = noCommand;
}
}
public void setCommand(int slot, Command onCommand, Command offCommand) {
// 边界检查
if (slot >= 0 && slot < slotSize) {
onCommands[slot] = onCommand;
offCommands[slot] = offCommand;
}
}
public void onButtonWasPushed(int slot) {
if (slot >= 0 && slot < slotSize) {
onCommands[slot].execute();
}
}
public void offButtonWasPushed(int slot) {
if (slot >= 0 && slot < slotSize) {
offCommands[slot].execute();
}
}
}
关灯命令:
public class LightOffCommand implements Command {
private final Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.off();
}
}
测试:
现在,遥控器上一共有两个按钮,一个是房间的灯,一个是客厅的灯。
public static void main(String[] args) {
RemoteControl remoteControl = new RemoteControl();
Light livingRoomLight = new Light("客厅");
Light roomLight = new Light("房间");
LightOnCommand livingRoomLightOnCommand = new LightOnCommand(livingRoomLight);
LightOffCommand livingRoomLightOffCommand = new LightOffCommand(livingRoomLight);
LightOnCommand roomLightOnCommand = new LightOnCommand(roomLight);
LightOffCommand roomLightOffCommand = new LightOffCommand(roomLight);
remoteControl.setCommand(0, livingRoomLightOnCommand, livingRoomLightOffCommand);
remoteControl.setCommand(1, roomLightOnCommand, roomLightOffCommand);
remoteControl.onButtonWasPushed(0);
remoteControl.onButtonWasPushed(1);
remoteControl.offButtonWasPushed(0);
remoteControl.offButtonWasPushed(1);
}
好了,这样就实现了一个多个按钮的遥控器,遥控器具体是怎么执行的,我们无需关注。只需要知道能满足我们的要求即可。
虽然,我们实现了一个多个按钮的遥控器。但它还是不够高级,不支持将命令组合起来使用。
Party模式
我现在想要开灯,要求一键开启客厅和房间的灯,关灯也是如此。
这里,我们想要实现这种功能,就要使用宏命令模式,把按钮的功能组合进去。
public class MacroCommand implements Command {
private final Command[] commands;
public MacroCommand(Command[] commands) {
this.commands = commands;
}
@Override
public void execute() {
for (Command command : commands) {
command.execute();
}
}
}
使用:
public static void main(String[] args) {
RemoteControl remoteControl = new RemoteControl();
Light livingRoomLight = new Light("客厅");
Light roomLight = new Light("房间");
Command[] onCommands = {new LightOnCommand(livingRoomLight), new LightOnCommand(roomLight)};
Command[] offCommands = {new LightOffCommand(livingRoomLight), new LightOffCommand(roomLight)};
MacroCommand macroOnCommand = new MacroCommand(onCommands);
MacroCommand macroOffCommand = new MacroCommand(offCommands);
remoteControl.setCommand(0, macroOnCommand, macroOffCommand);
remoteControl.onButtonWasPushed(0);
remoteControl.offButtonWasPushed(0);
}
这样就实现了宏命令的模式。就像开头列举的一个例子,想要打印出文件列表,只需要加上一个-v
参数就好了。
使用宏命令开关灯也是一样,比如想要开厨房的灯,和关厨房的灯,把厨房的命令加上就可以完成我们的需求。
这就是本篇文章要介绍的命令模式,使用场景确实感觉会比较少,不过对于一些执行操作耦合太紧时,或者遇到大量的if else,可以尝试使用命令模式。
不过,说到if else明明策略模式就可以解决,为什么还要使用命令模式呢?
在工作中很多设计模式都是结合着来使用的,用来解耦、提高编码素质、和代码的可读性等等。
设计模式虽好,但也不要为了使用设计模式而去过渡的设计,可能会造成明明很简单的一个功能,却因为过度设计而带来了不必要的复杂性。