定义
命令也可以理解为请求
降低 命令发送者和命令执行者 的耦合度。想想网站,你发送一个HTTP请求,你需要知道是哪个Controller执行吗?你可能知道后台谁来处理的吗?这不就是命令模式。
命令模式(Command Pattern):将一个请求封装为一个对象,从而让我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。命令模式是一种对象行为型模式,其别名为动作(Action)模式或事务(Transaction)模式。
问题
某桌面系统,设置快捷键F1功能为 打开帮助文档,于是写代码:
public class FunctionF1 {
private HelpHandler help; //HelpHandler:帮助文档处理类,请求接收者
//在FunctionButton的onClick()方法中调用HelpHandler的display()方法
public void onClick() {
help = new HelpHandler();
help.display(); //显示帮助文档
}
private class HelpHandler{
public void display(){}
}
}
1. 不同的人设置F1不同的功能。Tony设置F1为 重置功能:
问题:由于F1和HelpHandler直接耦合,就需要修改这个类。违反“开闭原则”。
2.增加F2的功能键:
问题:就需要增加一个F2的类,需要实现的功能也需要重新编写,会导致系统类迅速增加;由于F1和F2没有任何关系,则在其他地方调用这两个类时,必须依赖的是具体实现,不符合“依赖倒置原则”。
3. 用户需要动态新增功能,快捷键不知道,功能也不清楚:
问题:能否实现也是问题。
场景
用在命令的发送者和执行者进行分离,示例:团长,参谋长,营长
需要对发出的命令做记录,撤销,排队等操作。示例: 命令在参谋长没有下发都可以被撤销,这些命令都可以被记录以及下发的时候按照某种优先级进行排序都可以。
实现方式
团长(Client),参谋长(Invoker),营长(Reveiver)
团长(Invoker),传令兵(Command),营长(Reveiver)
还有角色:命令(Command)。第一种方式中,命令只是Entity,存储命令的。第二种方式中,传令兵功能简单和跟命令合并,命令称为真正的传递着。
区别:参谋长具有命令的处理能力,传令兵没有命令的处理能力,只是透传;命令的处理能力包括:优先级,记录,撤销,重做,事务等。
参谋长模式
类图
实现(com.haiwei.behavior.command.command1)
命令对象
public class Command {
private String cmd;
public String getCmd() {
return cmd;
}
public void setCmd(String cmd) {
this.cmd = cmd;
}
}
实行对象(参谋长)
public class Invoker {
private Map<String,Receiver> recieverMap = new HashMap<>();
public void redister(String cmd,Receiver reciever){
recieverMap.put(cmd, reciever);
}
public void exec(Command cmd){
if(recieverMap.containsKey(cmd.getCmd())){
recieverMap.get(cmd.getCmd()).exec(cmd);
return;
}
throw new RuntimeException("命令没有对应的功能");
}
}
命令执行者(营长)
public interface Receiver {
public void exec(Command cmd);
}
public class ReceiverF1 implements Receiver {
public void exec(Command cmd) {
System.out.println("exec F2 : " + cmd.getCmd());
}
}
public class ReceiverF2 implements Receiver {
public void exec(Command cmd) {
System.out.println("exec F2 : " + cmd.getCmd());
}
}
命令发出者(团长)
//需要设定命令和执行者的对应规则,然后发出命令,让Invoker执行
public class Test1 {
/**
* out.eg:
* exec F2 : F1
* exec F2 : F2
*/
public static void main(String[] args) {
Invoker Invoker = new Invoker();
//注册命令和接受者的关系
Invoker.redister("F1", new ReceiverF1());
Invoker.redister("F2", new ReceiverF2());
//创建命令
Command cmd = new Command();
cmd.setCmd("F1");
//执行命令
Invoker.exec(cmd);
cmd.setCmd("F2");
Invoker.exec(cmd);
}
}
传令兵(命令) 模式
实现(com.haiwei.behavior.command.command2)
传令兵(命令对象)
public abstract class Command {
private Receiver receiver; //维持一个对请求接收者对象的引用
public Command(Receiver receiver) {
this.receiver = receiver;
}
public void execute() {
receiver.action(); //调用请求接收者的业务处理方法action()
}
}
public class ConcreteCommand extends Command {
public ConcreteCommand(Receiver receiver) {
super(receiver);
}
}
命令发送者
public class Invoker {
private Command command;
//构造注入
public Invoker(Command command) {
this.command = command;
}
//业务方法,用于调用命令类的execute()方法
public void call() {
command.execute();
}
}
命令执行者
public interface Receiver {
public void action();
}
public class ReceiverF1 implements Receiver {
public void action() {
System.out.println("exec F1 ... ");
}
}
public class ReceiverF2 implements Receiver {
public void action() {
System.out.println("exec F2 ...");
}
}
运行
public class Test2 {
/**
* eg.out:exec F1 ...
*/
public static void main(String[] args) {
Receiver receiver = new ReceiverF1();
/*
* 封装命令和执行者关系,一次执行组装一次
* 对比一下参谋长模式:参谋长模式是一次设定规则,参谋长根据规则分发命令。
*/
Command cmd = new ConcreteCommand(receiver );
/*
* 团长执行命令,团长亲自来驱动执行
* 对比参谋长模式:命令的执行核心是参谋长执行,团长就定大的战略(命令规则)就行
*/
Invoker invoker = new Invoker(cmd);
invoker.call();
}
}
撤销命令
在参谋长模式中可以实现,在传令兵模式中直接驱动执行了,是没法撤销的。
参谋长中,撤销未执行的命令直接撤销就行;或者针对正在执行的命令可以使用事务的完整性进行回滚;针对已经执行的命令,可以按照执行数据记录进行恢复数据。
宏命令
宏命令(Macro Command)又称为组合命令,它是组合模式和命令模式联用的产物。宏命令是一个具体命令类,它拥有一个集合属性,在该集合中包含了对其他命令对象的引用。通常宏命令不直接与请求接收者交互,而是通过它的成员来调用接收者的方法。当调用宏命令的execute()方法时,将递归调用它所包含的每个成员命令的execute()方法,一个宏命令的成员可以是简单命令,还可以继续是宏命令。执行一个宏命令将触发多个具体命令的执行,从而实现对命令的批处理。