//: enumerated/VendingMachine.java
// {Args: VendingMachineInput.txt}
package ch19enums;
import java.util.*;
import net.mindview.util.*;
import static ch19enums.Input.*;
import static net.mindview.util.Print.*;
/**
* 自动贩卖机模拟状态机,用enum实现
*/
public class VendingMachine {
private static State state = State.RESTING;
private static int amount = 0;
private static Input selection = null;
/**
* 状态机,自动贩卖机模拟
*
* @param args
*/
public static void stateMachine(String[] args) {
Generator<Input> gen = new RandomInputGenerator();
if (args.length == 1)
gen = new FileInputGenerator(args[0]);
run(gen);
}
enum StateDuration {
TRANSIENT
} // Tagging enum
enum State {
RESTING {
void next(Input input) {
switch (Category.categorize(input)) {
case MONEY:
amount += input.amount();
state = ADDING_MONEY;
break;
case SHUT_DOWN:
state = TERMINAL;
default:
}
}
},
ADDING_MONEY {
void next(Input input) {
switch (Category.categorize(input)) {
case MONEY:
amount += input.amount();
break;
case ITEM_SELECTION:
selection = input;
if (amount < selection.amount())
print("Insufficient money for " + selection);
else
state = DISPENSING;
break;
case QUIT_TRANSACTION:
state = GIVING_CHANGE;
break;
case SHUT_DOWN:
state = TERMINAL;
default:
}
}
},
DISPENSING(StateDuration.TRANSIENT) {
void next() {
print("here is your " + selection);
amount -= selection.amount();
state = GIVING_CHANGE;
}
},
GIVING_CHANGE(StateDuration.TRANSIENT) {
void next() {
if (amount > 0) {
print("Your change: " + amount);
amount = 0;
}
state = RESTING;
}
},
TERMINAL {
void output() {
print("Halted");
}
};
private boolean isTransient = false;
State() {
}
State(StateDuration trans) {
isTransient = true;
}
void next(Input input) {
throw new RuntimeException("Only call "
+ "next(Input input) for non-transient states");
}
void next() {
throw new RuntimeException("Only call next() for "
+ "StateDuration.TRANSIENT states");
}
void output() {
print(amount);
}
}
static void run(Generator<Input> gen) {
while (state != State.TERMINAL) {
state.next(gen.next());
while (state.isTransient)
state.next();
state.output();
}
}
public static void main(String[] args) {
stateMachine(args);
}
}
// For a basic sanity check:
class RandomInputGenerator implements Generator<Input> {
public Input next() {
return Input.randomSelection();
}
}
// Create Inputs from a file of ';'-separated strings:
class FileInputGenerator implements Generator<Input> {
private Iterator<String> input;
public FileInputGenerator(String fileName) {
input = new TextFile(fileName, ";").iterator();
}
public Input next() {
if (!input.hasNext())
return null;
return Enum.valueOf(Input.class, input.next().trim());
}
} /*
* Output: 25 50 75 here is your CHIPS 0 100 200 here is your TOOTHPASTE 0 25 35
* Your change: 35 0 25 35 Insufficient money for SODA 35 60 70 75 Insufficient
* money for SODA 75 Your change: 75 0 Halted
*/// :~
enum Category {
MONEY(NICKEL, DIME, QUARTER, DOLLAR), ITEM_SELECTION(TOOTHPASTE, CHIPS,
SODA, SOAP), QUIT_TRANSACTION(ABORT_TRANSACTION), SHUT_DOWN(STOP);
private Input[] values;
Category(Input... types) {
values = types;
}
private static EnumMap<Input, Category> categories = new EnumMap<Input, Category>(
Input.class);
static {
for (Category c : Category.class.getEnumConstants())
for (Input type : c.values)
categories.put(type, c);
}
public static Category categorize(Input input) {
return categories.get(input);
}
}