This example is code that implements the behavior of a Gumball machine. The Main class is used to illustrate that the machine cannot dispense gumballs unless the prerequisites have been met, i.e., there is candy in the machine, money has been inserted into the machine and the handle turned. The first four steps in the Main class show the results of improperly attempting to get the candy and steps 5 through 8 show the behavior of the machine as the necessary operations required for the machine to dispense gumballs are performed. The result of running this code is shown in the ANT output.
public class Main {
public static void main(String[] args) {
GumballMachine machine = new GumballMachine();
System.out.println("1. turning handle: " + machine.turnHandle());
System.out.println("2. inserting money: " + machine.insertMoney());
System.out.println("3. turning handle: " + machine.turnHandle());
System.out.println("4. taking gumball: " + machine.takeGumballs());
System.out.println("5. adding gumballs: " + machine.addGumballs());
System.out.println("6. inserting money: " + machine.insertMoney());
System.out.println("7. turning handle: " + machine.turnHandle());
System.out.println("8. taking gumball: " + machine.takeGumballs());
}
}public class GumballMachine {
GumballMachineState state;
// constructor
public GumballMachine() {
state = new HasNoGumballsState();
}
GumballMachine addGumballs() {
state = state.addGumballs();
return this;
}
GumballMachine insertMoney() {
state = state.insertMoney();
return this;
}
GumballMachine turnHandle() {
state = state.turnHandle();
return this;
}
GumballMachine takeGumballs() {
state = state.takeGumballs();
return this;
}
@Override
public String toString() {
return state.toString();
}
}public abstract class GumballMachineState {
GumballMachineState addGumballs() {
return this;
}
GumballMachineState insertMoney() {
return this;
}
GumballMachineState turnHandle() {
return this;
}
GumballMachineState takeGumballs() {
return this;
}
}public class HasNoGumballsState extends GumballMachineState {
@Override
HasNoMoneyState addGumballs() {
return new HasNoMoneyState();
}
@Override
public String toString() {
return "Please add gumballs (" + this.getClass().getName() + ")";
}
}public class HasNoMoneyState extends GumballMachineState {
@Override
HandleNotTurnedState insertMoney() {
return new HandleNotTurnedState();
}
@Override
public String toString() {
return "Please insert money (" + this.getClass().getName() + ")";
}
}public class HandleNotTurnedState extends GumballMachineState {
@Override
GumballsNotTakenState turnHandle() {
return new GumballsNotTakenState();
}
@Override
public String toString() {
return "Please turn handle (" + this.getClass().getName() + ")";
}
}public class GumballsNotTakenState extends GumballMachineState {
@Override
HasNoMoneyState takeGumballs() {
return new HasNoMoneyState();
}
@Override
public String toString() {
return "Please take gumballs (" + this.getClass().getName() + ")";
}
}statemachine> ant
Buildfile: build.xml
clean:
[delete] Deleting directory /Users/greghelton/dev/src/java/StateMachine/build
[delete] Deleting directory /Users/greghelton/dev/src/java/StateMachine/dist
compile:
[mkdir] Created dir: /Users/greghelton/dev/src/java/StateMachine/build
[javac] Compiling 7 source files to /Users/greghelton/dev/src/java/StateMachine/build
jar:
[mkdir] Created dir: /Users/greghelton/dev/src/java/StateMachine/dist
[jar] Building jar: /Users/greghelton/dev/src/java/StateMachine/dist/StateMachine.jar
run:
[java] 1. turning handle: Please add gumballs (HasNoGumballsState)
[java] 2. inserting money: Please add gumballs (HasNoGumballsState)
[java] 3. turning handle: Please add gumballs (HasNoGumballsState)
[java] 4. taking gumball: Please add gumballs (HasNoGumballsState)
[java] 5. adding gumballs: Please insert money (HasNoMoneyState)
[java] 6. inserting money: Please turn handle (HandleNotTurnedState)
[java] 7. turning handle: Please take gumballs (GumballsNotTakenState)
[java] 8. taking gumball: Please insert money (HasNoMoneyState)
BUILD SUCCESSFUL
Total time: 0 seconds
statemachine>Note that this rather complex behavior has been implemented without a single conditional statement. Conditional logic can certainly be added if necessary. The turnHandle() method of HandleNotTurnedState could 'return this' under some conditions and, under other conditions, 'return new GumballsNotTakenState()'.
The abstract class GumballMachineState acts to block all changes of state. Each of the classes that extend GumballMachineState override one of its methods and return a new instance of a subclass of GumballMachineState. These subclasses allow the state of the Gumball machine to be changed.