命令模式的目的
将服务请求封装成对象,从而可以不同的方式对请求进行操作。
它使client可以调用命令但不需要了解命令的执行细节。并且可以对命令进行修改而无需影响调用命令的client程序。
命令模式的结构
Command
声明执行操作的接口
ConcreteCommand
定义Receiver对象和动作的绑定
通过调用Receiver的相应操作实现Execute()方法
Client
创建ConcreteCommand对象从而设置相应的receiver
Invoker
要求命令执行用户请求
Receiver
负责解析用户的请求并执行相应的操作
命令模式的类图
下面看一个例子(例子来源:https://github.com/iluwatar/java-design-patterns)
男巫(wizard)施加命令(command)给小怪。二具体的命令(invisibility spell,shrink spell)是让小怪(target)不可见或者变大或变小及取消施加命令和重新施加。
调用者
public class Wizard {
private static final Logger LOGGER = LoggerFactory.getLogger(Wizard.class);
private Deque<Command> undoStack = new LinkedList<>();
private Deque<Command> redoStack = new LinkedList<>();
public Wizard() {
// comment to ignore sonar issue: LEVEL critical
}
/**
* Cast spell
*/
public void castSpell(Command command, Target target) {
LOGGER.info("{} casts {} at {}", this, command, target);
command.execute(target);
undoStack.offerLast(command);
}
/**
* Undo last spell
*/
public void undoLastSpell() {
if (!undoStack.isEmpty()) {
Command previousSpell = undoStack.pollLast();
redoStack.offerLast(previousSpell);
LOGGER.info("{} undoes {}", this, previousSpell);
previousSpell.undo();
}
}
/**
* Redo last spell
*/
public void redoLastSpell() {
if (!redoStack.isEmpty()) {
Command previousSpell = redoStack.pollLast();
undoStack.offerLast(previousSpell);
LOGGER.info("{} redoes {}", this, previousSpell);
previousSpell.redo();
}
}
@Override
public String toString() {
return "Wizard";
}
抽象的命令类
public abstract class Command {
public abstract void execute(Target target);
public abstract void undo();
public abstract void redo();
@Override
public abstract String toString();
}
命令InvisibilitySpell 隐身
public class InvisibilitySpell extends Command {
private Target target;
@Override
public void execute(Target target) {
target.setVisibility(Visibility.INVISIBLE);
this.target = target;
}
@Override
public void undo() {
if (target != null) {
target.setVisibility(Visibility.VISIBLE);
}
}
@Override
public void redo() {
if (target != null) {
target.setVisibility(Visibility.INVISIBLE);
}
}
@Override
public String toString() {
return "Invisibility spell";
}
}
命令类ShrinkSpell 变小,收缩
public class ShrinkSpell extends Command {
private Size oldSize;
private Target target;
@Override
public void execute(Target target) {
oldSize = target.getSize();
target.setSize(Size.SMALL);
this.target = target;
}
@Override
public void undo() {
if (oldSize != null && target != null) {
Size temp = target.getSize();
target.setSize(oldSize);
oldSize = temp;
}
}
@Override
public void redo() {
undo();
}
@Override
public String toString() {
return "Shrink spell";
}
}
public abstract class Target {
private static final Logger LOGGER = LoggerFactory.getLogger(Target.class);
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;
}
@Override
public abstract String toString();
/**
* Print status
*/
public void printStatus() {
LOGGER.info("{}, [size={}] [visibility={}]", this, getSize(), getVisibility());
}
}
小怪Goblin ,被施加命令的对象
public class Goblin extends Target {
public Goblin() {
setSize(Size.NORMAL);
setVisibility(Visibility.VISIBLE);
}
@Override
public String toString() {
return "Goblin";
}
}
客户端调用 通过施加不同的命令,取消命令或重新施加命令,然后打印目标的状态查看命令是否施加成功。
public class App {
/**
* Program entry point
*
* @param args command line args
*/
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();
}
}
命令模式总结
- 命令模式实现了调用操作的对象与具体实现操作的对象之间的解耦
- 命令可以像其他对象一样被操作和扩展
- 命令可以被组合为一个复合命令