命令模式
说句实话,这个模式还算比较简单。笔者当年还没有接触过设计模式的时候就已然用起了命令模式。这个模式跟之前的策略模式有异曲同工之处,这里就简单说明一下这两个模式的不同之处。
在之前的文章中也介绍了过了策略模式(可以参考此链接),这个模式主要是用用于在对象中变化的行为,于是我们就把行为抽象成接口,然后让不同的行为实现去“覆盖”接口,这样调用起来就是我们希望的行为实现。而在命令模式中也是使用接口解耦合,那不同的就是,命令模式只会是单一的调用接口中的方法,以达到“命令”的目的。这里的接口的实现实际上是一种“包装”,是把不同的行为(或命令) 包装成一个能被唯一方法调用的实现类,这样命令模式中可以不用在意具体是怎么实现的,只要给我此接口的实现,我直接调用特定的方法就可以了,至于里面实现到底调用那些“组合”,都不是我要关心的。此外,命令模式要比策略模式更加“灵活”一点,怎么说?在我们用策略模式的时候,我们关注的是行为的变化,也就是说,行为在随着业务的需要不断变化以及扩展,但是缺点是行为的种类或者方式只有固定,一旦行为的种类发生变化,那就需要额外的修改原有的对象以达到目的,而命令模式就不需要到底有哪些“命令”,或者种类多少,实现的时候都会被“命令”这个接口做一层包装。所以命令模式相比较而言更加灵活一点。但是命令模式也是有一定缺陷,那就是命令模式对于接口的调用是单一的,也就是说,命令模式下,调用的方法是单一的或者说是固定好的,如果多出一个方法或者有别的方法需要实现,那逻辑就需要更改了。对于不同的设计模式要懂得灵活运用,要对项目的整体框架以及业务有一定的了解。
还是老样子,下面给出一命令模式的代码示例。这里我们假设我们要为一个遥控器写一个控制程序,这个遥控器很特别,里面有很多插槽,可以理解为每个插槽可以被不同的控制器插入以获得某个物件的控制。例如,我们可以插入电灯的控制,遥控器就可以控制开关了,现在问题来了,我们有很多的物件控制,那遥控器要怎么识别呢,毕竟每个控件的方法并不一样,例如
public class Light {
public void on () {
System.out.println("light is on");
}
public void off () {
System.out.println("light is off");
}
}
这是一个电灯的开关,里面有on和off的开关。在如
public class TV {
public void turnOn () {
System.out.println("tv is on");
}
public void turnOff () {
System.out.println("tv is off");
}
public void setChannel () {
System.out.println("set another channel");
}
}
这是一个TV控件,里面的方法跟电灯的就不一样了。那如果没有用设计模式,恐怕你的遥控器代码会是这样的
public class RemoteController {
Object command;
public RemoteController (Object command) {
this.command = command;
}
public void on () {
if (command instanceof Light) {
((Light) command).on();
} else if (command instanceof TV) {
((TV)command).turnOn();
}
}
}
就需要一个一个去判断是什么类型,这样显然是不行。那正确的姿势是什么样的呢?
首先我们要先定义一个命令接口
public interface Command {
void execute();
}
命令接口里只有一个execute方法。
下面我就需要进行“包装”了。例如
public class LightCommand implements Command{
Light light;
public LightCommand (Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
}
这里我们把电灯这个控件进行了包装,在execute方法里我们放入了我们需要对电灯做处理的任何方法(如果是TV的话可能是先调用turnOn方法,然后调用setChannel默认频道) 可以根据业务需要进行任意搭配。
这样,我们的遥控器就不需要管插入的控件是什么了,只要执行execute方法即可。
public class RemoteController {
Command command;
public RemoteController (Command command) {
this.command = command;
}
public void on () {
command.execute();
}
}
这样是不是很清晰明了呢。
PS:对于了解Java8的同学来说,可能这里不太需要命令模式了,因为Java8的函数式编程已经可以代替命令模式了。其实上例中,Command接口就是一个函数式接口,只要有了这个接口,直接传入lambda表达式就可以了。