命令模式是一种行为型设计模式。在命令模式中,所有的请求都会被包装成为一个对象。
参考了一下其他关于命令模式的文章,其中有谈到说是可以用不同的请求对客户进行参数化。我对这句话的理解是,因为将请求封装成为对象,所以客户的所有操作,其实就是多个命令类的对象而已,即参数化了。
命令模式的最大的特点就是将请求的调用者与请求的最终执行者进行了解耦。调用者需要关心的仅仅是请求对象是否被执行了,对于请求对象是如何执行的,对什么进行操作的,统统不需要关心。
原理:命令模式中,一般有如下几个角色:
- command:命令的抽象接口,其中包含execute方法。根据业务需求,有的还会包含其他通用方法如undo等。
- concreteCommand:具体的命令实现类。每一种请求,都会映射一个具体的命令实现类。对于每个类,都会实现execute方法,并且依赖receiver,也就是接收者对象。execute方法中,一般就是调用接收者对象的对应方法,从而实现对请求的最终处理。
- receiver:请求的接收者,也是请求的最终的执行者,被命令实现类所依赖。
- invoker:请求的调用者。调用者会调用所有传入的命令对象的execute方法,开启命令的执行,但是不会与最终的执行者receive耦合,两者中间是通过命令实现类进行联系和沟通的。
- client:进行接收者对象和命令对象的创建,并建立两者之间的联系。
适用场景:涉及到“命令”、“操作”或者“控制”的场景,一般都是命令模式的适用场景。
- 餐厅点菜的过程,消费者(client)说要吃某几种菜(命令对象),赶快做好端上来。服务员(invoker)会记录所有点过的菜品(保存所有的命令对象),然后将订单给后厨说,按照单子做(调用所有命令对象的execute)。之后就会启动每一道菜品的制作流程。对于菜品如何烹制,与服务员是没有关系的,两者不耦合。
- 遥控器的运行过程也可以理解成是一种命令模式的应用。假设有一个智能家居的遥控器,在面板上,可以控制电灯的开关,空调的开关(各种命令对象)。遥控器就是invoker的角色,负责实际命令的调用。而最终命令的执行,则是各种电器(receiver)来进行的。
- 多线程的执行,也可以理解为一种命令模式的应用。
案例
背景
我们以智能家居的遥控器为例。手头上有一个智能遥控器(invoker),能够控制打开电灯,关闭电灯,打开电视,关闭电视等操作。
实现定义命令接口
public interface Command {
void execute();
}
定义receiver
public class Light {
public void open()
{
System.out.println("Light is open");
}
public void close()
{
System.out.println("Light is closed");
}
}
public class TV {
public void open()
{
System.out.println("TV is open");
}
public void close()
{
System.out.println("TV is closed");
}
}
定义具体命令
public class OpenLignt implements Command {
private Light light;
public OpenLignt(Light light) {
this.light = light;
}
@Override
public void execute() {
this.light.open();
}
}
public class CloseLight implements Command {
private Light light;
public CloseLight(Light light) {
this.light = light;
}
@Override
public void execute() {
this.light.close();
}
}
public class OpenTV implements Command {
private TV tv;
public OpenTV(TV tv) {
this.tv = tv;
}
@Override
public void execute() {
tv.open();
}
}
public class CloseTV implements Command {
private TV tv;
public CloseTV(TV tv) {
this.tv = tv;
}
@Override
public void execute() {
tv.close();
}
}
定义遥控器(invoker)
public class Controller {
private Command[] buttons;
private int num = 0;
public Controller() {
buttons = new Command[10];
}
public void addButton(Command command)
{
buttons[num++] = command;
}
public void pushButton(int number)
{
if (number > -1 && number < buttons.length)
{
buttons[number].execute();
}
}
}
验证程序
public class Test {
public static void main(String[] args){
Light light = new Light();
TV tv = new TV();
Controller controller = new Controller();
controller.addButton(new OpenLignt(light));
controller.addButton(new CloseLight(light));
controller.addButton(new OpenTV(tv));
controller.addButton(new CloseTV(tv));
controller.pushButton(0);
controller.pushButton(1);
controller.pushButton(2);
controller.pushButton(3);
}
}
程序输出
扩展Light is open
Light is closed
TV is open
TV is closedProcess finished with exit code 0
命令模式中有一种扩展,叫做宏命令,能同时进行一组命令的执行。比如遥控器只存在两个按键,一个控制所有电器的开启,一个控制所有电器的关闭。那么我们不需要改动已有的代码,只要扩展一个组合命令类,其中包含多个命令即可。
public class MutilCommand implements Command {
private Command[] commands;
public MutilCommand(Command[] commands) {
this.commands = commands;
}
@Override
public void execute() {
for (Command command : commands)
{
command.execute();
}
}
}
验证程序
public class Test {
public static void main(String[] args){
Light light = new Light();
TV tv = new TV();
Controller controller = new Controller();
controller.addButton(new OpenLignt(light));
controller.addButton(new CloseLight(light));
controller.addButton(new OpenTV(tv));
controller.addButton(new CloseTV(tv));
controller.addButton(new MutilCommand(new Command[]{new OpenLignt(light), new OpenTV(tv)}));
controller.addButton(new MutilCommand(new Command[]{new CloseLight(light), new CloseTV(tv)}));
controller.pushButton(0);
controller.pushButton(1);
controller.pushButton(2);
controller.pushButton(3);
System.out.println("");
controller.pushButton(4);
controller.pushButton(5);
}
}
运行结果
总结Light is open
Light is closed
TV is open
TV is closedLight is open
TV is open
Light is closed
TV is closedProcess finished with exit code 0
命令模式的核心思想就是将命令或者请求封装成对象,分离请求调用者和请求最终执行者。
优点:将请求调用者和执行者解耦。
缺点:如果存在较多的命令或者请求,需要较多的命令类。