Merge pull request #1531 from ravening/command-functional
Refactor the command pattern to use lambda functions
This commit is contained in:
		@@ -48,18 +48,16 @@ public class Wizard {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  public Wizard() {}
 | 
					  public Wizard() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void castSpell(Command command, Target target) {
 | 
					  public void castSpell(Runnable runnable) {
 | 
				
			||||||
    LOGGER.info("{} casts {} at {}", this, command, target);
 | 
					    runnable.run();
 | 
				
			||||||
    command.execute(target);
 | 
					    undoStack.offerLast(runnable);
 | 
				
			||||||
    undoStack.offerLast(command);
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void undoLastSpell() {
 | 
					  public void undoLastSpell() {
 | 
				
			||||||
    if (!undoStack.isEmpty()) {
 | 
					    if (!undoStack.isEmpty()) {
 | 
				
			||||||
      var previousSpell = undoStack.pollLast();
 | 
					      var previousSpell = undoStack.pollLast();
 | 
				
			||||||
      redoStack.offerLast(previousSpell);
 | 
					      redoStack.offerLast(previousSpell);
 | 
				
			||||||
      LOGGER.info("{} undoes {}", this, previousSpell);
 | 
					      previousSpell.run();
 | 
				
			||||||
      previousSpell.undo();
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -67,8 +65,7 @@ public class Wizard {
 | 
				
			|||||||
    if (!redoStack.isEmpty()) {
 | 
					    if (!redoStack.isEmpty()) {
 | 
				
			||||||
      var previousSpell = redoStack.pollLast();
 | 
					      var previousSpell = redoStack.pollLast();
 | 
				
			||||||
      undoStack.offerLast(previousSpell);
 | 
					      undoStack.offerLast(previousSpell);
 | 
				
			||||||
      LOGGER.info("{} redoes {}", this, previousSpell);
 | 
					      previousSpell.run();
 | 
				
			||||||
      previousSpell.redo();
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -79,84 +76,7 @@ public class Wizard {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Next we present the spell hierarchy.
 | 
					Next, we have the goblin who's the target of the spells.
 | 
				
			||||||
 | 
					 | 
				
			||||||
```java
 | 
					 | 
				
			||||||
public interface Command {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  void execute(Target target);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  void undo();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  void redo();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  String toString();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public class InvisibilitySpell implements 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 implements 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";
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Finally, we have the goblin who's the target of the spells.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
```java
 | 
					```java
 | 
				
			||||||
public abstract class Target {
 | 
					public abstract class Target {
 | 
				
			||||||
@@ -203,33 +123,73 @@ public class Goblin extends Target {
 | 
				
			|||||||
    return "Goblin";
 | 
					    return "Goblin";
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void changeSize() {
 | 
				
			||||||
 | 
					    var oldSize = getSize() == Size.NORMAL ? Size.SMALL : Size.NORMAL;
 | 
				
			||||||
 | 
					    setSize(oldSize);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void changeVisibility() {
 | 
				
			||||||
 | 
					    var visible = getVisibility() == Visibility.INVISIBLE
 | 
				
			||||||
 | 
					          ? Visibility.VISIBLE : Visibility.INVISIBLE;
 | 
				
			||||||
 | 
					    setVisibility(visible);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Finally we have the wizard in main function who casts spell
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```java
 | 
				
			||||||
 | 
					public static void main(String[] args) {
 | 
				
			||||||
 | 
					  var wizard = new Wizard();
 | 
				
			||||||
 | 
					  var goblin = new Goblin();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // casts shrink/unshrink spell
 | 
				
			||||||
 | 
					  wizard.castSpell(goblin::changeSize);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // casts visible/invisible spell
 | 
				
			||||||
 | 
					  wizard.castSpell(goblin::changeVisibility);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // undo and redo casts
 | 
				
			||||||
 | 
					   wizard.undoLastSpell();
 | 
				
			||||||
 | 
					   wizard.redoLastSpell();
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Here's the whole example in action.
 | 
					Here's the whole example in action.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```java
 | 
					```java
 | 
				
			||||||
var wizard = new Wizard();
 | 
					var wizard = new Wizard();
 | 
				
			||||||
var goblin = new Goblin();
 | 
					var goblin = new Goblin();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
goblin.printStatus();
 | 
					goblin.printStatus();
 | 
				
			||||||
wizard.castSpell(new ShrinkSpell(), goblin);
 | 
					wizard.castSpell(goblin::changeSize);
 | 
				
			||||||
goblin.printStatus();
 | 
					goblin.printStatus();
 | 
				
			||||||
wizard.castSpell(new InvisibilitySpell(), goblin);
 | 
					
 | 
				
			||||||
 | 
					wizard.castSpell(goblin::changeVisibility);
 | 
				
			||||||
goblin.printStatus();
 | 
					goblin.printStatus();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
wizard.undoLastSpell();
 | 
					wizard.undoLastSpell();
 | 
				
			||||||
goblin.printStatus();
 | 
					goblin.printStatus();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					wizard.undoLastSpell();
 | 
				
			||||||
 | 
					goblin.printStatus();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					wizard.redoLastSpell();
 | 
				
			||||||
 | 
					goblin.printStatus();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					wizard.redoLastSpell();
 | 
				
			||||||
 | 
					goblin.printStatus();
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Here's the program output:
 | 
					Here's the program output:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```java
 | 
					```java
 | 
				
			||||||
// Goblin, [size=normal] [visibility=visible]
 | 
					Goblin, [size=normal] [visibility=visible]
 | 
				
			||||||
// Wizard casts Shrink spell at Goblin
 | 
					Goblin, [size=small] [visibility=visible]
 | 
				
			||||||
// Goblin, [size=small] [visibility=visible]
 | 
					Goblin, [size=small] [visibility=invisible]
 | 
				
			||||||
// Wizard casts Invisibility spell at Goblin
 | 
					Goblin, [size=small] [visibility=visible]
 | 
				
			||||||
// Goblin, [size=small] [visibility=invisible]
 | 
					Goblin, [size=normal] [visibility=visible]
 | 
				
			||||||
// Wizard undoes Invisibility spell
 | 
					Goblin, [size=small] [visibility=visible]
 | 
				
			||||||
// Goblin, [size=small] [visibility=visible]
 | 
					Goblin, [size=small] [visibility=invisible]
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Class diagram
 | 
					## Class diagram
 | 
				
			||||||
 
 | 
				
			|||||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 50 KiB  | 
@@ -1,15 +1,6 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
<class-diagram version="1.1.8" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true"
 | 
					<class-diagram version="1.1.8" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true"
 | 
				
			||||||
  realizations="true" associations="true" dependencies="false" nesting-relationships="true">
 | 
					  realizations="true" associations="true" dependencies="false" nesting-relationships="true">
 | 
				
			||||||
  <class id="1" language="java" name="com.iluwatar.command.ShrinkSpell" project="command" 
 | 
					 | 
				
			||||||
    file="/command/src/main/java/com/iluwatar/command/ShrinkSpell.java" binary="false" corner="BOTTOM_RIGHT">    
 | 
					 | 
				
			||||||
    <position height="178" width="141" x="-30" y="681"/>    
 | 
					 | 
				
			||||||
    <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" 
 | 
					 | 
				
			||||||
      sort-features="false" accessors="true" visibility="true">      
 | 
					 | 
				
			||||||
      <attributes public="true" package="true" protected="true" private="true" static="true"/>      
 | 
					 | 
				
			||||||
      <operations public="true" package="true" protected="true" private="true" static="true"/>    
 | 
					 | 
				
			||||||
    </display>  
 | 
					 | 
				
			||||||
  </class>  
 | 
					 | 
				
			||||||
  <class id="2" language="java" name="com.iluwatar.command.Goblin" project="command"
 | 
					  <class id="2" language="java" name="com.iluwatar.command.Goblin" project="command"
 | 
				
			||||||
    file="/command/src/main/java/com/iluwatar/command/Goblin.java" binary="false" corner="BOTTOM_RIGHT">
 | 
					    file="/command/src/main/java/com/iluwatar/command/Goblin.java" binary="false" corner="BOTTOM_RIGHT">
 | 
				
			||||||
    <position height="-1" width="-1" x="129" y="1223"/>
 | 
					    <position height="-1" width="-1" x="129" y="1223"/>
 | 
				
			||||||
@@ -28,24 +19,6 @@
 | 
				
			|||||||
      <operations public="true" package="true" protected="true" private="true" static="true"/>
 | 
					      <operations public="true" package="true" protected="true" private="true" static="true"/>
 | 
				
			||||||
    </display>
 | 
					    </display>
 | 
				
			||||||
  </class>
 | 
					  </class>
 | 
				
			||||||
  <class id="4" language="java" name="com.iluwatar.command.Command" project="command" 
 | 
					 | 
				
			||||||
    file="/command/src/main/java/com/iluwatar/command/Command.java" binary="false" corner="BOTTOM_RIGHT">    
 | 
					 | 
				
			||||||
    <position height="-1" width="-1" x="129" y="561"/>    
 | 
					 | 
				
			||||||
    <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" 
 | 
					 | 
				
			||||||
      sort-features="false" accessors="true" visibility="true">      
 | 
					 | 
				
			||||||
      <attributes public="true" package="true" protected="true" private="true" static="true"/>      
 | 
					 | 
				
			||||||
      <operations public="true" package="true" protected="true" private="true" static="true"/>    
 | 
					 | 
				
			||||||
    </display>  
 | 
					 | 
				
			||||||
  </class>  
 | 
					 | 
				
			||||||
  <class id="5" language="java" name="com.iluwatar.command.InvisibilitySpell" project="command" 
 | 
					 | 
				
			||||||
    file="/command/src/main/java/com/iluwatar/command/InvisibilitySpell.java" binary="false" corner="BOTTOM_RIGHT">    
 | 
					 | 
				
			||||||
    <position height="160" width="141" x="151" y="681"/>    
 | 
					 | 
				
			||||||
    <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" 
 | 
					 | 
				
			||||||
      sort-features="false" accessors="true" visibility="true">      
 | 
					 | 
				
			||||||
      <attributes public="true" package="true" protected="true" private="true" static="true"/>      
 | 
					 | 
				
			||||||
      <operations public="true" package="true" protected="true" private="true" static="true"/>    
 | 
					 | 
				
			||||||
    </display>  
 | 
					 | 
				
			||||||
  </class>  
 | 
					 | 
				
			||||||
  <class id="6" language="java" name="com.iluwatar.command.Target" project="command"
 | 
					  <class id="6" language="java" name="com.iluwatar.command.Target" project="command"
 | 
				
			||||||
    file="/command/src/main/java/com/iluwatar/command/Target.java" binary="false" corner="BOTTOM_RIGHT">
 | 
					    file="/command/src/main/java/com/iluwatar/command/Target.java" binary="false" corner="BOTTOM_RIGHT">
 | 
				
			||||||
    <position height="-1" width="-1" x="129" y="1014"/>
 | 
					    <position height="-1" width="-1" x="129" y="1014"/>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,33 +4,11 @@ package com.iluwatar.command {
 | 
				
			|||||||
    + App()
 | 
					    + App()
 | 
				
			||||||
    + main(args : String[]) {static}
 | 
					    + main(args : String[]) {static}
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  interface Command {
 | 
					 | 
				
			||||||
    + Command()
 | 
					 | 
				
			||||||
    + execute(Target) {abstract}
 | 
					 | 
				
			||||||
    + redo() {abstract}
 | 
					 | 
				
			||||||
    + toString() : String {abstract}
 | 
					 | 
				
			||||||
    + undo() {abstract}
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  class Goblin {
 | 
					  class Goblin {
 | 
				
			||||||
    + Goblin()
 | 
					    + Goblin()
 | 
				
			||||||
    + toString() : String
 | 
					    + toString() : String
 | 
				
			||||||
  }
 | 
					    + changeSize()
 | 
				
			||||||
  class InvisibilitySpell {
 | 
					    + changeVisibility()
 | 
				
			||||||
    - target : Target
 | 
					 | 
				
			||||||
    + InvisibilitySpell()
 | 
					 | 
				
			||||||
    + execute(target : Target)
 | 
					 | 
				
			||||||
    + redo()
 | 
					 | 
				
			||||||
    + toString() : String
 | 
					 | 
				
			||||||
    + undo()
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  class ShrinkSpell {
 | 
					 | 
				
			||||||
    - oldSize : Size
 | 
					 | 
				
			||||||
    - target : Target
 | 
					 | 
				
			||||||
    + ShrinkSpell()
 | 
					 | 
				
			||||||
    + execute(target : Target)
 | 
					 | 
				
			||||||
    + redo()
 | 
					 | 
				
			||||||
    + toString() : String
 | 
					 | 
				
			||||||
    + undo()
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  enum Size {
 | 
					  enum Size {
 | 
				
			||||||
    + NORMAL {static}
 | 
					    + NORMAL {static}
 | 
				
			||||||
@@ -62,22 +40,19 @@ package com.iluwatar.command {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
  class Wizard {
 | 
					  class Wizard {
 | 
				
			||||||
    - LOGGER : Logger {static}
 | 
					    - LOGGER : Logger {static}
 | 
				
			||||||
    - redoStack : Deque<Command>
 | 
					    - redoStack : Deque<Runnable>
 | 
				
			||||||
    - undoStack : Deque<Command>
 | 
					    - undoStack : Deque<Runnable>
 | 
				
			||||||
    + Wizard()
 | 
					    + Wizard()
 | 
				
			||||||
    + castSpell(command : Command, target : Target)
 | 
					    + castSpell(Runnable : runnable)
 | 
				
			||||||
    + redoLastSpell()
 | 
					    + redoLastSpell()
 | 
				
			||||||
    + toString() : String
 | 
					    + toString() : String
 | 
				
			||||||
    + undoLastSpell()
 | 
					    + undoLastSpell()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
Target -->  "-size" Size
 | 
					Target -->  "-size" Size
 | 
				
			||||||
Wizard -->  "-undoStack" Command
 | 
					Wizard -->  "-changeSize" Goblin
 | 
				
			||||||
ShrinkSpell -->  "-oldSize" Size
 | 
					Wizard -->  "-changeVisibility" Goblin
 | 
				
			||||||
InvisibilitySpell -->  "-target" Target
 | 
					 | 
				
			||||||
ShrinkSpell -->  "-target" Target
 | 
					 | 
				
			||||||
Target -->  "-visibility" Visibility
 | 
					Target -->  "-visibility" Visibility
 | 
				
			||||||
Goblin --|> Target
 | 
					Goblin --|> Target
 | 
				
			||||||
InvisibilitySpell ..|> Command
 | 
					App --> "castSpell" Wizard
 | 
				
			||||||
ShrinkSpell ..|> Command
 | 
					 | 
				
			||||||
@enduml
 | 
					@enduml
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,12 +30,10 @@ package com.iluwatar.command;
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * <p>Four terms always associated with the command pattern are command, receiver, invoker and
 | 
					 * <p>Four terms always associated with the command pattern are command, receiver, invoker and
 | 
				
			||||||
 * client. A command object (spell) knows about the receiver (target) and invokes a method of the
 | 
					 * client. A command object (spell) knows about the receiver (target) and invokes a method of the
 | 
				
			||||||
 * receiver. Values for parameters of the receiver method are stored in the command. The receiver
 | 
					 * receiver. An invoker object (wizard) receives a reference to the command to be executed and
 | 
				
			||||||
 * then does the work. An invoker object (wizard) knows how to execute a command, and optionally
 | 
					 * optionally does bookkeeping about the command execution. The invoker does not know anything
 | 
				
			||||||
 * does bookkeeping about the command execution. The invoker does not know anything about a concrete
 | 
					 * about how the command is executed. The client decides which commands to execute at which
 | 
				
			||||||
 * command, it knows only about command interface. Both an invoker object and several command
 | 
					 * points. To execute a command, it passes a reference of the function to the invoker object.
 | 
				
			||||||
 * objects are held by a client object (app). The client decides which commands to execute at which
 | 
					 | 
				
			||||||
 * points. To execute a command, it passes the command object to the invoker object.
 | 
					 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * <p>In other words, in this example the wizard casts spells on the goblin. The wizard keeps track
 | 
					 * <p>In other words, in this example the wizard casts spells on the goblin. The wizard keeps track
 | 
				
			||||||
 * of the previous spells cast, so it is easy to undo them. In addition, the wizard keeps track of
 | 
					 * of the previous spells cast, so it is easy to undo them. In addition, the wizard keeps track of
 | 
				
			||||||
@@ -54,10 +52,10 @@ public class App {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    goblin.printStatus();
 | 
					    goblin.printStatus();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    wizard.castSpell(new ShrinkSpell(), goblin);
 | 
					    wizard.castSpell(goblin::changeSize);
 | 
				
			||||||
    goblin.printStatus();
 | 
					    goblin.printStatus();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    wizard.castSpell(new InvisibilitySpell(), goblin);
 | 
					    wizard.castSpell(goblin::changeVisibility);
 | 
				
			||||||
    goblin.printStatus();
 | 
					    goblin.printStatus();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    wizard.undoLastSpell();
 | 
					    wizard.undoLastSpell();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,37 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
 * The MIT License
 | 
					 | 
				
			||||||
 * Copyright © 2014-2019 Ilkka Seppälä
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
					 | 
				
			||||||
 * of this software and associated documentation files (the "Software"), to deal
 | 
					 | 
				
			||||||
 * in the Software without restriction, including without limitation the rights
 | 
					 | 
				
			||||||
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
					 | 
				
			||||||
 * copies of the Software, and to permit persons to whom the Software is
 | 
					 | 
				
			||||||
 * furnished to do so, subject to the following conditions:
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * The above copyright notice and this permission notice shall be included in
 | 
					 | 
				
			||||||
 * all copies or substantial portions of the Software.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
					 | 
				
			||||||
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
					 | 
				
			||||||
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
					 | 
				
			||||||
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
					 | 
				
			||||||
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
					 | 
				
			||||||
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
					 | 
				
			||||||
 * THE SOFTWARE.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package com.iluwatar.command;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Interface for Commands.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public interface Command {
 | 
					 | 
				
			||||||
  void execute(Target target);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  void undo();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  void redo();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  String toString();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -23,11 +23,16 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.iluwatar.command;
 | 
					package com.iluwatar.command;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Goblin is the target of the spells.
 | 
					 * Goblin is the target of the spells.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class Goblin extends Target {
 | 
					public class Goblin extends Target {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static final Logger LOGGER = LoggerFactory.getLogger(Goblin.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public Goblin() {
 | 
					  public Goblin() {
 | 
				
			||||||
    setSize(Size.NORMAL);
 | 
					    setSize(Size.NORMAL);
 | 
				
			||||||
    setVisibility(Visibility.VISIBLE);
 | 
					    setVisibility(Visibility.VISIBLE);
 | 
				
			||||||
@@ -37,5 +42,4 @@ public class Goblin extends Target {
 | 
				
			|||||||
  public String toString() {
 | 
					  public String toString() {
 | 
				
			||||||
    return "Goblin";
 | 
					    return "Goblin";
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,57 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
 * The MIT License
 | 
					 | 
				
			||||||
 * Copyright © 2014-2019 Ilkka Seppälä
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
					 | 
				
			||||||
 * of this software and associated documentation files (the "Software"), to deal
 | 
					 | 
				
			||||||
 * in the Software without restriction, including without limitation the rights
 | 
					 | 
				
			||||||
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
					 | 
				
			||||||
 * copies of the Software, and to permit persons to whom the Software is
 | 
					 | 
				
			||||||
 * furnished to do so, subject to the following conditions:
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * The above copyright notice and this permission notice shall be included in
 | 
					 | 
				
			||||||
 * all copies or substantial portions of the Software.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
					 | 
				
			||||||
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
					 | 
				
			||||||
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
					 | 
				
			||||||
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
					 | 
				
			||||||
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
					 | 
				
			||||||
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
					 | 
				
			||||||
 * THE SOFTWARE.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package com.iluwatar.command;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * InvisibilitySpell is a concrete command.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public class InvisibilitySpell implements 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";
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,59 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
 * The MIT License
 | 
					 | 
				
			||||||
 * Copyright © 2014-2019 Ilkka Seppälä
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
					 | 
				
			||||||
 * of this software and associated documentation files (the "Software"), to deal
 | 
					 | 
				
			||||||
 * in the Software without restriction, including without limitation the rights
 | 
					 | 
				
			||||||
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
					 | 
				
			||||||
 * copies of the Software, and to permit persons to whom the Software is
 | 
					 | 
				
			||||||
 * furnished to do so, subject to the following conditions:
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * The above copyright notice and this permission notice shall be included in
 | 
					 | 
				
			||||||
 * all copies or substantial portions of the Software.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
					 | 
				
			||||||
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
					 | 
				
			||||||
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
					 | 
				
			||||||
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
					 | 
				
			||||||
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
					 | 
				
			||||||
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
					 | 
				
			||||||
 * THE SOFTWARE.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package com.iluwatar.command;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * ShrinkSpell is a concrete command.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public class ShrinkSpell implements 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";
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -62,4 +62,21 @@ public abstract class Target {
 | 
				
			|||||||
  public void printStatus() {
 | 
					  public void printStatus() {
 | 
				
			||||||
    LOGGER.info("{}, [size={}] [visibility={}]", this, getSize(), getVisibility());
 | 
					    LOGGER.info("{}, [size={}] [visibility={}]", this, getSize(), getVisibility());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Changes the size of the target.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public void changeSize() {
 | 
				
			||||||
 | 
					    var oldSize = getSize() == Size.NORMAL ? Size.SMALL : Size.NORMAL;
 | 
				
			||||||
 | 
					    setSize(oldSize);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Changes the visibility of the target.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public void changeVisibility() {
 | 
				
			||||||
 | 
					    var visible = getVisibility() == Visibility.INVISIBLE
 | 
				
			||||||
 | 
					            ? Visibility.VISIBLE : Visibility.INVISIBLE;
 | 
				
			||||||
 | 
					    setVisibility(visible);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,20 +35,18 @@ public class Wizard {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  private static final Logger LOGGER = LoggerFactory.getLogger(Wizard.class);
 | 
					  private static final Logger LOGGER = LoggerFactory.getLogger(Wizard.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private final Deque<Command> undoStack = new LinkedList<>();
 | 
					  private final Deque<Runnable> undoStack = new LinkedList<>();
 | 
				
			||||||
  private final Deque<Command> redoStack = new LinkedList<>();
 | 
					  private final Deque<Runnable> redoStack = new LinkedList<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public Wizard() {
 | 
					  public Wizard() {
 | 
				
			||||||
    // comment to ignore sonar issue: LEVEL critical
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Cast spell.
 | 
					   * Cast spell.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public void castSpell(Command command, Target target) {
 | 
					  public void castSpell(Runnable runnable) {
 | 
				
			||||||
    LOGGER.info("{} casts {} at {}", this, command, target);
 | 
					    runnable.run();
 | 
				
			||||||
    command.execute(target);
 | 
					    undoStack.offerLast(runnable);
 | 
				
			||||||
    undoStack.offerLast(command);
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@@ -58,8 +56,7 @@ public class Wizard {
 | 
				
			|||||||
    if (!undoStack.isEmpty()) {
 | 
					    if (!undoStack.isEmpty()) {
 | 
				
			||||||
      var previousSpell = undoStack.pollLast();
 | 
					      var previousSpell = undoStack.pollLast();
 | 
				
			||||||
      redoStack.offerLast(previousSpell);
 | 
					      redoStack.offerLast(previousSpell);
 | 
				
			||||||
      LOGGER.info("{} undoes {}", this, previousSpell);
 | 
					      previousSpell.run();
 | 
				
			||||||
      previousSpell.undo();
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -70,8 +67,7 @@ public class Wizard {
 | 
				
			|||||||
    if (!redoStack.isEmpty()) {
 | 
					    if (!redoStack.isEmpty()) {
 | 
				
			||||||
      var previousSpell = redoStack.pollLast();
 | 
					      var previousSpell = redoStack.pollLast();
 | 
				
			||||||
      undoStack.offerLast(previousSpell);
 | 
					      undoStack.offerLast(previousSpell);
 | 
				
			||||||
      LOGGER.info("{} redoes {}", this, previousSpell);
 | 
					      previousSpell.run();
 | 
				
			||||||
      previousSpell.redo();
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -56,10 +56,10 @@ public class CommandTest {
 | 
				
			|||||||
    var wizard = new Wizard();
 | 
					    var wizard = new Wizard();
 | 
				
			||||||
    var goblin = new Goblin();
 | 
					    var goblin = new Goblin();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    wizard.castSpell(new ShrinkSpell(), goblin);
 | 
					    wizard.castSpell(goblin::changeSize);
 | 
				
			||||||
    verifyGoblin(goblin, GOBLIN, Size.SMALL, Visibility.VISIBLE);
 | 
					    verifyGoblin(goblin, GOBLIN, Size.SMALL, Visibility.VISIBLE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    wizard.castSpell(new InvisibilitySpell(), goblin);
 | 
					    wizard.castSpell(goblin::changeVisibility);
 | 
				
			||||||
    verifyGoblin(goblin, GOBLIN, Size.SMALL, Visibility.INVISIBLE);
 | 
					    verifyGoblin(goblin, GOBLIN, Size.SMALL, Visibility.INVISIBLE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    wizard.undoLastSpell();
 | 
					    wizard.undoLastSpell();
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user