设计模式之命令模式(Command)
本篇为 https://github.com/iluwatar/java-design-patterns/tree/master/command 阅读笔记
典型用例
保持请求的历史
实现回调功能
实现撤消功能
目的
将请求封装为对象,从而允许使用不同的请求,队列或日志请求参数化客户端,并支持可撤销操作
场景
精灵有两种属性(大小,可见不可见),巫师可以操作这两种属性,并且可以撤销,重做(Ctrl+z,Ctrl+y),如果再加几个属性或再加几个其他物种呢?要保证可扩展性需要采用命令模式。
封装目标对象(精灵,野兽),把对目标对象属性的操作设置成命令(excute,undo,redo),巫师调用命令操作对象,这样避免了巫师直接调用目标对象,简化了操作,新的命令可以很容易添加到系统中,降低了类之间的耦合度。
目标属性枚举类型
public enum Size {
SMAL("smal"),NORMAL("normal");
private String title;
Size(String title){
this.title = title;
}
}
public enum Visibility {
VISIBLE("visible"), INVISIBLE("invisible");
private String title;
Visibility(String title) {
this.title = title;
}
@Override
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;
}
@Override
public abstract String toString();
public void printStatus() {
System.out.println(String.format("%s [size = %s] [visibility = %s]", this, size, visibility));
}
}
精灵
public class Goblin extends Target {
public Goblin(){
setSize(Size.NORMAL);
setVisibility(Visibility.VISIBLE);
}
@Override
public String toString() {
return "Goblin";
}
}
命令抽象类
public abstract class Command {
public abstract void excute(Target target);
public abstract void undo();
public abstract void redo();
@Override
public abstract String toString();
}
大小操作命令
public class ShrinkSpell extends Command {
private Size oldSize;
private Target target;
@Override
public void excute(Target target) {
oldSize = target.getSize();
target.setSize(Size.SMAL);
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 "ShrinkSpell";
}
}
可见性操作命令
public class InvisibilitySpell extends Command {
private Target target;
@Override
public void excute(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";
}
}
巫师对命令进行封装
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(String.format("%s casts %s at %s", this, command, target));
command.excute(target);
undoStack.offerLast(command);
}
public void undoLastSpell() {
if (!undoStack.isEmpty()) {
Command previousSpell = undoStack.pollLast();
redoStack.offerLast(previousSpell);
System.out.println(String.format("%s undo %s", this, previousSpell));
previousSpell.undo();
}
}
public void redoLastSpell() {
if (!redoStack.isEmpty()) {
Command prevoiuSpell = redoStack.pollLast();
System.out.printf("%s redoes %s %n",this,prevoiuSpell);
prevoiuSpell.redo();
}
}
@Override
public String toString() {
return "Wizard";
}
}
测试
@Test
public void commandTest(){
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();
}
类图
缺点:使用命令模式可能会导致某些系统有过多的具体命令类。