定义
将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。
典型使用场景
- 维护请求历史
- 实现回调功能
- 实现撤消功能
例子
借鉴一个游戏:哥布林小妖精是游戏的主人公之一,用户可以给他施展符咒,让它变大变小,也可让它隐身重现。
首先定义大小与隐身相关的常量:
// 可视的常量
public enum Visibility {
VISIBLE("visible"), INVISIBLE("invisible"), UNDEFINED("");
private String title;
Visibility(String title) {
this.title = title;
}
public String toString() {
return title;
}
}
// 大小的常量
public enum Size {
SMALL("small"), NORMAL("normal"), LARGE("large"), UNDEFINED("");
private String title;
Size(String title) {
this.title = title;
}
public String toString() {
return title;
}
}
目标可以是哥布林,也可以是其他,因此提供一个基类,哥布林是其一个子类:
// 目标的抽象基类
public abstract class Target {
private Size size;
private Visibility visibility;
public Size getSize() {
return size;
}
public void setSize(Size size) {
this.size = size;
}
public Visibility getVisibility() {
return visibility;
}
public void setVisibility(Visibility visibility) {
this.visibility = visibility;
}
public abstract String toString();
public void printStatus() {
System.out.println(String.format("%s, [size=%s] [visibility=%s]", this, getSize(),
getVisibility()));
System.out.println();
}
}
// 哥布林小妖精是目标实例
public class Goblin extends Target {
public Goblin() {
setSize(Size.NORMAL);
setVisibility(Visibility.VISIBLE);
}
public String toString() {
return "Goblin";
}
}
定义命令的抽象对象,隐藏和缩小符咒都是继承自这个对象,每个对象都有执行、撤销和重做三个方法:
public abstract class Command {
public abstract void execute(Target target); // 执行
public abstract void undo(); // 撤销
public abstract void redo(); // 重做
public abstract String toString();
}
// 具体命令:隐藏符咒
public class InvisibilitySpell extends Command {
private Target target;
public void execute(Target target) {
target.setVisibility(Visibility.INVISIBLE);
this.target = target;
}
public void undo() {
if (target != null) {
target.setVisibility(Visibility.VISIBLE);
}
}
public void redo() {
if (target != null) {
target.setVisibility(Visibility.INVISIBLE);
}
}
public String toString() {
return "Invisibility spell";
}
}
// 具体命令:缩小符咒
public class ShrinkSpell extends Command {
private Size oldSize;
private Target target;
public void execute(Target target) {
oldSize = target.getSize();
target.setSize(Size.SMALL);
this.target = target;
}
public void undo() {
if (oldSize != null && target != null) {
Size temp = target.getSize();
target.setSize(oldSize);
oldSize = temp;
}
}
public void redo() {
undo();
}
public String toString() {
return "Shrink spell";
}
}
有了目标以及命令对象,创建一个类,维护命令列表:
// 命令的调用者
public class Wizard {
// 双向队列
private Deque<Command> undoStack = new LinkedList<>();
private Deque<Command> redoStack = new LinkedList<>();
public Wizard() {}
// 执行符咒
public void castSpell(Command command, Target target) {
System.out.println(this + " casts " + command + " at " + target);
command.execute(target);
undoStack.offerLast(command);
}
// 撤销上次执行的符咒
public void undoLastSpell() {
if (!undoStack.isEmpty()) {
Command previousSpell = undoStack.pollLast();
redoStack.offerLast(previousSpell);
System.out.println(this + " undoes " + previousSpell);
previousSpell.undo();
}
}
// 重新执行上个符咒
public void redoLastSpell() {
if (!redoStack.isEmpty()) {
Command previousSpell = redoStack.pollLast();
undoStack.offerLast(previousSpell);
System.out.println(this + " redoes " + previousSpell);
previousSpell.redo();
}
}
public String toString() {
return "Wizard";
}
}
进行测试:
public class App {
public static void main(String[] args) {
Wizard wizard = new Wizard();
Goblin goblin = new Goblin();
goblin.printStatus();
wizard.castSpell(new ShrinkSpell(), goblin);
goblin.printStatus();
wizard.castSpell(new InvisibilitySpell(), goblin);
goblin.printStatus();
wizard.undoLastSpell();
goblin.printStatus();
wizard.undoLastSpell();
goblin.printStatus();
wizard.redoLastSpell();
goblin.printStatus();
wizard.redoLastSpell();
goblin.printStatus();
}
}
分析
命令模式可以维护操作历史列表,便于进行撤销、重做等操作。