1 定义:
1.1 定义:Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.(将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能)。
1.2 通用类图:
个人认为以下类图更利于理解:
类图中包含的Client从来不是用作测试的,这里的client是为了将“请求”与“执行者”对应起来,若使用此模式,则可以依次准备好:Receiver、Command、Invoker,最后直接调用Invoker的方法。所以这里,为便于理解,将其类图作以细化。
1.3 通用代码:
注意上述client的静态main方法为人为添加的测试,非原模式的一部分。
public abstract class Receiver {
// 抽象接收者,定义每个接收者都必须完成的业务
public abstract void action();
}
public class ConcreteReceiver1 extends Receiver {
// 每个接受者都必须处理一定的业务逻辑
public void action() {
}
}
public abstract class Command {
// 每个命令类都必须有一个执行命令的方法
public abstract void execute();
}
public class ConcreteCommand1 extends Command {
// 也对那个Receiver类进行命令处理
private Receiver receiver;
// 构造函数传递接收者
public ConcreteCommand1(Receiver _receiver) {
this.receiver = _receiver;
}
// 每个具体的命令都必须实现一个命令
public void execute() {
// 业务处理
this.receiver.action();
}
}
public class Invoker {
private Command command;
// 受气包,接受命令
public void setCommand(Command _command) {
this.command = _command;
}
// 执行命令
public void doSomething() {
this.command.execute();
}
}
public class Client {
public static Command prepareCommand() {
// 定义接收者
Receiver receiver = new ConcreteReceiver1();
// 定义一个发送给接收者的命令
Command command = new ConcreteCommand1(receiver);
return command;
}
public static void main(String[] args) {
// 首先声明出调用者Invoker
Invoker invoker = new Invoker();
// 把命令交给调用者去执行
invoker.setCommand(prepareCommand());
invoker.doSomething();
}
}
2 优点
2.1 类间解耦:调用者与接收者角色之间没有任何依赖关系,调用者实现功能时只须调用Command抽象类的execute方法就可以,不需要了解到底是哪个接收者执行。
2.2 可扩展性:Command的子类可以非常容易地扩展,而调用者Invoker和高层次的模块Client不产生严重的代码耦合。
3 缺点
如果命令多,Command的子类就多,这个类会膨胀很大,需慎用。
4 应用场景
触发-反馈机制处理的场所。
5 注意事项
6 扩展
6.1 撤销-重做:这种涉及状态改变的情况,可以有以下两种方法解决。
A:结合备忘录模式还原最后状态,该方法适合接收者为状态的变更情况,而不适合事件处理。
B:通过增加一个新的命令,实现事件的回滚。
6.2 对于接收者不多的情形下,我们可以适当地将某一接收者作为默认参数,实在需要改变接收者时再重新设置。这样可以减少高层模块对低层模块的依赖关系,提高系统的稳定性。
7 范例
7.1撤销-重做(仅支持单步撤销的计算数字的例子)
类图如下:
源代码如下:
package _09_Command.example;
public abstract class Command {
Receiver receiver;
int parm;
public Command(Receiver rec, int parm) {
this.receiver = rec;
this.parm = parm;
}
public abstract void execute();
}
public class AddCommand extends Command {
public AddCommand(Receiver rec, int parm) {
super(rec, parm);
}
@Override
public void execute() {
receiver.action(CMD.ADD, parm);
}
}
public class SubCommand extends Command {
public SubCommand(Receiver rec, int parm) {
super(rec, parm);
}
@Override
public void execute() {
receiver.action(CMD.SUB, parm);
}
}
public class MulCommand extends Command {
public MulCommand(Receiver rec, int parm) {
super(rec, parm);
}
@Override
public void execute() {
receiver.action(CMD.MUL, parm);
}
}
public class DivCommand extends Command {
public DivCommand(Receiver rec, int parm) {
super(rec, parm);
}
@Override
public void execute() {
receiver.action(CMD.DIV, parm);
}
}
public class SetCommand extends Command {
public SetCommand(Receiver rec, int parm) {
super(rec, parm);
}
@Override
public void execute() {
receiver.action(CMD.SET, parm);
}
}
public class UndoCommand extends Command {
public UndoCommand(Receiver rec, int parm) {
super(rec, 0);
}
@Override
public void execute() {
receiver.action(CMD.UNDO, 0);
}
}
enum CMD {
ADD, SUB, MUL, DIV, SET, UNDO
};
public abstract class Receiver {
protected int result = 0;
protected int lastParm;
protected CMD lastAction;
public abstract void action(CMD cmd, int parm);
}
public class ConcreteReceiver extends Receiver {
@Override
public void action(CMD cmd, int parm) {
switch (cmd) {
case ADD:
System.out.print(result + " + " + parm + " = ");
result += parm;
lastParm = parm;
lastAction = CMD.ADD;
System.out.println(result);
System.out
.println("----------------------------------------------");
break;
case SUB:
System.out.print(result + " - " + parm + " = ");
result -= parm;
lastParm = parm;
lastAction = CMD.SUB;
System.out.println(result);
System.out
.println("----------------------------------------------");
break;
case MUL:
System.out.print(result + " x " + parm + " = ");
result *= parm;
lastParm = parm;
lastAction = CMD.MUL;
System.out.println(result);
System.out
.println("----------------------------------------------");
break;
case DIV:
System.out.print(result + " / " + parm + " = ");
result /= parm;
lastParm = parm;
lastAction = CMD.DIV;
System.out.println(result);
System.out
.println("----------------------------------------------");
break;
case SET:
result = parm;
lastParm = 0;
lastAction = CMD.SET;
System.out.println("清除并设置值: " + result);
System.out
.println("----------------------------------------------");
break;
case UNDO:
if (lastAction != CMD.UNDO && lastAction != CMD.SET) {
switch (lastAction) {
case ADD:
result -= lastParm;
break;
case SUB:
result += lastParm;
break;
case MUL:
result /= lastParm;
break;
case DIV:
result *= lastParm;
break;
}
lastAction = CMD.UNDO;
System.out.println("撤销上步操作后值: " + result);
System.out
.println("----------------------------------------------");
}
break;
}
}
}
public class Invoker {
Command command;
public void action() {
command.execute();
}
public void setCommand(Command cmd) {
command = cmd;
}
}
public class Client {
public static void main(String[] args) {// // just for test
Receiver rec = new ConcreteReceiver();
Command cmds[] = { new SetCommand(rec, 5), new AddCommand(rec, 5),
new SubCommand(rec, 5), new UndoCommand(rec, 5),
new MulCommand(rec, 5), new DivCommand(rec, 5) };
Invoker inv = new Invoker();
for (int i = 0; i < 6; i++) {
inv.setCommand(cmds[i]);
inv.action();
}
}
}
结果如下:
清除并设置值: 5
----------------------------------------------
5 + 5 = 10
----------------------------------------------
10 - 5 = 5
----------------------------------------------
撤销上步操作后值: 10
----------------------------------------------
10 x 5 = 50
----------------------------------------------
50 / 5 = 10
----------------------------------------------