动机
- 在软件构建过程中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合一一比如需要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。
- 在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。
模式定义
将一个请求(行为)封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
类图
总结
- Command模式的根本目的在于将“行为请求者”与“行为实现者”解耦,在面向对象语言中,常见的实现手段是“将行为抽象为对象”。
- 实现Command接口的具体命令对象ConcreteCommand有时候根据需要可能会保存一些额外的状态信息。通过使用Composite模式,可以将多个“命令”封装为一个复合命令“MacroCommand”。
- Command模式与Java中的函数式接口类似,Runnable接口就是典型的命令模式。
实例
场景
使用遥控器控制电视机,把开、关、换台的请求封装在不同的命令对象中。
- 定义命令接口
public interface ITVCommand {
// 用来调用接受者的方法
void action(TVMachine machine);
// 命令对应的行为
void execute();
}
- 实现命令,封装命令对应的行为(也可以不使用action,直接在接收者里面实现好,execute里面直接调用就好,定义action是为了接收者稳定,以后直接扩展command就可以了,不需要修改接收者代码)
public class TurnOnTV implements ITVCommand {
@Override
public void action(TVMachine machine) {
machine.process(this);
}
@Override
public void execute() {
System.out.println("Open TV");
}
}
public class TurnOffTV implements ITVCommand {
@Override
public void action(TVMachine machine) {
machine.process(this);
}
@Override
public void execute() {
System.out.println("Close TV");
}
}
public class ChangeChannelTV implements ITVCommand {
@Override
public void action(TVMachine machine) {
machine.process(this);
}
@Override
public void execute() {
System.out.println("Change channel TV");
}
}
- 定义接收者角色类
public class TVMachine {
public void process(ITVCommand command) {
command.execute();
}
}
- 定义invoke角色类
public class Controller {
private TVMachine machine;
public Controller(TVMachine machine) {
this.machine = machine;
}
public void action(ITVCommand command) {
command.action(machine);
}
}
- 定义客户端调用类
public class Client {
public static void main(String[] args) {
TVMachine machine = new TVMachine();
Controller controller = new Controller(machine);
controller.action(new TurnOnTV());
controller.action(new ChangeChannelTV());
controller.action(new TurnOffTV());
}
}