命令模式是什么呢?考虑一下电视遥控器的场景,我们按下遥控器的某个按键,电视就会有相应的反应(例如换台)。按下遥控器就想是人发起了一个命令,遥控器接受了这个命令,并将其转发给电视,最后电视处理这个命令,并作出响应。整个过程中,遥控器并不知道请求的具体内容,即它不知道人类是想要换台呢,还是调节音量等等,它不关系这些东西而只需要将命令完整的转发给电视机即可。
那为什么还需要这个遥控器呢?人类完全可以直接操作电视机的嘛(注意电视机上提供的按钮其实也属于遥控器)。确实可以,但是这样一来,人类和电视机之间的联系太紧密了,即耦合度高。如果现在电视机的实现变了,人类就不得不重新学习如何操作电视机,而如果用遥控器的话,人类只需要熟悉遥控器上的按钮,即使电视机的实现改变了,只要其接口没变,遥控器都能很好的工作。
命令模式的实现
下面是上述场景的简单代码实现:
//电视类
public class TV {
public void doChange() {
System.out.println("change TV");
}
}
//命令接口和实现类
public interface Command {
void execute();
}
public class ChangeCommand implements Command {
//需要指定该命令的接受对象
private TV tv;
public ChangeCommand(TV tv) {
this.tv = tv;
}
@Override
public void execute() {
this.tv.doChange();
}
}
//遥控器类
public class Controller {
//这里Commond可以设置为数组,使其支持多个命令,这里为了简单,直接单个命令了
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void action() {
this.command.execute();
}
}
//客户端
public class Client {
public static void main(String[] args) {
TV tv = new TV();
Command changeCommand = new ChangeCommand(tv);
Controller controller = new Controller();
controller.setCommand(changeCommand);
controller.action();
}
}
运行程序,可以得到如下结果:
change TV
显然,这是我们想要的结果。
命令模式剖析
命令模式分为五个部分:
- Client客户端(Client)
- Command接口 (Client)
- Command实现类 (ChangeCommand)
- invoker调用者(遥控器)
- Recevier接受者(TV)
类图如下所示:
命令模式的特点
命令模式很适合命令-响应的场景,其扩展性和复用性都很好,我们可以添加很多命令类,而且这些命令类可以相互组合形成一个更复杂的命令。即后面的扩展往往是建立在已有的命令类上进行的,这就使得其复用性很好。
其封装性也比较好,其实整个模式算是有两次封装,一次是在invoker里调用命令的execute()方法,另一次是在command的实现类里调用Receiver里的具体方法。(上述例子中是doChange()方法)。这样使得客户端只需要知道命令的实现类是怎样就可以了,完全不用关系Receiver(TV)是如何实现的,也不用关心Invoker(遥控器)是怎么实现的。
命令模式的缺点主要是可能会导致命令类很多,而且如果命令很简单,那么这个类可能只有几行代码,如果不加限制可能导致“类爆炸”。
小结
现在可以给出命令模式的定义了:命令模式将请求封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。
本系列文章参考书籍是《Head First 设计模式》,文中代码示例出自书中。