java编程思想恶心的enum状态机示例
下面是一个包装输入的类
package test; import java.util.Random; public enum Input { NICKEL(5) , DIME(10) , QUARTER(25),DOLLAR(100), TOOTHPASTE(200),CHIPS(75),SODA(100),SOAP(50), ABOUT_TRANSACTION{ @Override int amount() { throw new RuntimeException("ABOUT_TRANSACTION") ; } } , STOP { @Override int amount() { throw new RuntimeException("SHUT_DOWN.amount()") ; } } ; private int value ; private Input(){} private Input(int value ){ this.value = value ; } int amount(){ return value ; } /** * 随机生成输入 */ static Random random = new Random() ; public static Input randomSelection(){ /** * 产生 0 ~ values().length区间内的数 , 包括0 不包括values().length */ return values()[random.nextInt(values().length )] ; } public static void main(String[] args) { for(int i = 0 ; i < 10 ; i ++){ System.out.println(Input.randomSelection()); } } }
在书中(第四版)values()[random.nextInt(values().length)]写错了,后面多减了一个1,
下面是主类,真不知道作者天天在想什么,竟然能想出这样的设计 --!
package test; import static test.Input.ABOUT_TRANSACTION; import static test.Input.CHIPS; import static test.Input.DIME; import static test.Input.DOLLAR; import static test.Input.NICKEL; import static test.Input.QUARTER; import static test.Input.SOAP; import static test.Input.SODA; import static test.Input.STOP; import static test.Input.TOOTHPASTE; import java.util.EnumMap; /** * * 状态转换 * * 模拟自动售货机的运行 * */ /** * 该类是为了获得输入命令的类别 */ enum Category{ MONEY(NICKEL,DIME,QUARTER,DOLLAR), ITEM_SELECTION(TOOTHPASTE,CHIPS,SODA,SOAP), QUIT_TRANSACTION(ABOUT_TRANSACTION), SHUT_DOWN(STOP) ; private Input[] values ; private Category(Input... inputs ){ values = inputs ; } private static EnumMap<Input,Category> categories = new EnumMap<Input,Category>(Input.class) ; static { for( Category c : Category.class.getEnumConstants()){ for( Input i : c.values){ categories.put(i, c) ; } } } public static Category getCategory(Input input ){ return categories.get(input) ; } } public class VendingMachine { /** * 模拟状态之间的转换 * */ private static int amount = 0 ; private static State state = State.RESTING ; private static Input selection = null ; enum StateDuration {TRANSIENT} enum State { RESTING { void next(Input input ){ switch(Category.getCategory(input)){ case MONEY : amount += input.amount() ; state = ADDING_MONEY ; break ; case SHUT_DOWN: state = TERMINAL ; break ; default : } } }, ADDING_MONEY { void next(Input input ){ switch(Category.getCategory(input)){ case MONEY : amount += input.amount() ; /** * 这里为什么不要设置state的值? * 因为当前已经是ADDING_MONEY状态,设置了以后还是这个状态,所以不需要设置 */ break ; case ITEM_SELECTION : selection = input ; if(amount < input.amount()){ System.out.println("Insufficient money for " + selection ); }else{ state = DISPENSING ; } break ; case QUIT_TRANSACTION : state = GIVING_CHANGE ; break ; case SHUT_DOWN : state = TERMINAL ; break ; default : } } }, DISPENSING(StateDuration.TRANSIENT){ void next(){ System.out.println("Here is your " + selection ) ; amount -= selection.amount() ; state = GIVING_CHANGE ; } }, GIVING_CHANGE(StateDuration.TRANSIENT){ void next(){ if(amount > 0 ){ System.out.println("you change : " + amount ) ; amount = 0 ; } state = RESTING ; } }, TERMINAL { void output(){ System.out.println("Halted!"); } } ; private boolean isTransaction = false ; void next(){ } void next(Input input ){ throw new RuntimeException("Only call next(Input input) for non-transient states ") ; } void output(){System.out.println(amount);} State(){} State(StateDuration trans ){ this.isTransaction = true ; } } public static void run(Generator<Input> gen ){ /** * 如果在前面执行的命令中是state变为terminal,则程序结束。 * 让其变为terminal的情况为输入的命令为STOP即SHUT_DOWN类别 */ while(state != State.TERMINAL ){ /** * 输入随机产生命令 , 当输入的命令非transaction类型的命令时,抛出异常 ,这在默认的next中设定 * 这里的state不可能是TERMINAL,也不可能是Transaction(因为下面那个while无限循环), * 所以永远不会执行那个会抛出异常的next方法。 */ state.next(gen.next()) ; /** * 判断该命令的剩下执行是否还需要命令,Transaction是true表示下面将要执行的任务不需要再输入命令 * 所以使用无限循环将该命令执行完,然后再输出余额 */ while(state.isTransaction){ state.next() ; } state.output() ; } } public static void main(String[] args) { Generator<Input> gen = new RandomInputGenerator() ; run(gen) ; } } class RandomInputGenerator implements Generator<Input>{ @Override public Input next() { return Input.randomSelection() ; } }