#590 add explanation for Command pattern
This commit is contained in:
parent
ee654cf7b2
commit
4264f52d49
@ -12,9 +12,214 @@ tags:
|
||||
Action, Transaction
|
||||
|
||||
## Intent
|
||||
Encapsulate a request as an object, thereby letting you
|
||||
parameterize clients with different requests, queue or log requests, and
|
||||
support undoable operations.
|
||||
Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.
|
||||
|
||||
## Explanation
|
||||
Real world example
|
||||
|
||||
> There is a wizard casting spells on a goblin. The spells are executed on the goblin one by one. The first spell shrinks the goblin and the second makes him invisible. Then the wizard reverses the spells one by one. Each spell here is a command object that can be undone.
|
||||
|
||||
In plain words
|
||||
|
||||
> Storing requests as command objects allows performing an action or undoing it at a later time.
|
||||
|
||||
Wikipedia says
|
||||
|
||||
> In object-oriented programming, the command pattern is a behavioral design pattern in which an object is used to encapsulate all information needed to perform an action or trigger an event at a later time.
|
||||
|
||||
**Programmatic Example**
|
||||
|
||||
Here's the sample code with wizard and goblin. Let's start from the wizard class.
|
||||
|
||||
```java
|
||||
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() {}
|
||||
|
||||
public void castSpell(Command command, Target target) {
|
||||
LOGGER.info("{} casts {} at {}", this, command, target);
|
||||
command.execute(target);
|
||||
undoStack.offerLast(command);
|
||||
}
|
||||
|
||||
public void undoLastSpell() {
|
||||
if (!undoStack.isEmpty()) {
|
||||
var previousSpell = undoStack.pollLast();
|
||||
redoStack.offerLast(previousSpell);
|
||||
LOGGER.info("{} undoes {}", this, previousSpell);
|
||||
previousSpell.undo();
|
||||
}
|
||||
}
|
||||
|
||||
public void redoLastSpell() {
|
||||
if (!redoStack.isEmpty()) {
|
||||
var previousSpell = redoStack.pollLast();
|
||||
undoStack.offerLast(previousSpell);
|
||||
LOGGER.info("{} redoes {}", this, previousSpell);
|
||||
previousSpell.redo();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Wizard";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Next we present the spell hierarchy.
|
||||
|
||||
```java
|
||||
public abstract class Command {
|
||||
|
||||
public abstract void execute(Target target);
|
||||
|
||||
public abstract void undo();
|
||||
|
||||
public abstract void redo();
|
||||
|
||||
@Override
|
||||
public abstract String toString();
|
||||
}
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
var temp = target.getSize();
|
||||
target.setSize(oldSize);
|
||||
oldSize = temp;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void redo() {
|
||||
undo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Shrink spell";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And last we have the goblin who's the target of the spells.
|
||||
|
||||
```java
|
||||
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();
|
||||
|
||||
public void printStatus() {
|
||||
LOGGER.info("{}, [size={}] [visibility={}]", this, getSize(), getVisibility());
|
||||
}
|
||||
}
|
||||
|
||||
public class Goblin extends Target {
|
||||
|
||||
public Goblin() {
|
||||
setSize(Size.NORMAL);
|
||||
setVisibility(Visibility.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Goblin";
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Finally here's the whole example in action.
|
||||
|
||||
```java
|
||||
var wizard = new Wizard();
|
||||
var goblin = new Goblin();
|
||||
goblin.printStatus();
|
||||
// Goblin, [size=normal] [visibility=visible]
|
||||
wizard.castSpell(new ShrinkSpell(), goblin);
|
||||
// Wizard casts Shrink spell at Goblin
|
||||
goblin.printStatus();
|
||||
// Goblin, [size=small] [visibility=visible]
|
||||
wizard.castSpell(new InvisibilitySpell(), goblin);
|
||||
// Wizard casts Invisibility spell at Goblin
|
||||
goblin.printStatus();
|
||||
// Goblin, [size=small] [visibility=invisible]
|
||||
wizard.undoLastSpell();
|
||||
// Wizard undoes Invisibility spell
|
||||
goblin.printStatus();
|
||||
// Goblin, [size=small] [visibility=visible]
|
||||
```
|
||||
|
||||
## Class diagram
|
||||

|
||||
|
Loading…
x
Reference in New Issue
Block a user