public enum Input { NICKEL(5), DIME(10), QUARTER(25), DOLLAR(100), TOOTHPASTER(200), CHIPS(75), SODA(100), SOAP(50), ABORT_TRANSACTION{ public int amount(){ //Disallow throw new RuntimeException("ABORT.acmount()"); } }, STOP{ // This must be the last instance public int amount(){ throw new RuntimeException("SHUT_DOWN.amount()"); } }; int value; Input(int value){ this.value = value; } Input(){} // abstract int amount(); int amount(){ return value; //In cents } static Random rand = new Random(47); public static Input randomSelection(){ // Don;t include STOP; return values()[rand.nextInt(values().length - 1)]; } }
除了2个特殊的Input实例之外,其他的Input都有相应的价格,因此在接口中
定义了amount()方法。然而, 对那两个特殊Input实例而言,调用amount()方法
并不合适,所以如果调用则抛出异常。
package enumerated; import java.util.EnumMap; import java.util.Iterator; import Container.Generator; import static enumerated.Input.*; enum Category{ MONEY(NICKEL, DIME, QUARTER), TIME_SELECTION( 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); } } public class VendingMachine { private static State state = State.RESTING; private static int amount = 0; private static Input selection = null; enum StateDuration{TRANSIENT} 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 TIME_SELECTION: selection = input; if(amount < selection.amount()) System.out.println("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(){ System.out.println("hers is your "+selection); amount -= selection.amount(); state = GIVING_CHANGE; } }, GIVING_CHANGE(StateDuration.TRANSIENT){ void next(){ if(amount > 0){ System.out.println("Your change: "+amount); amount = 0; } state = RESTING; } }, TERMINAL{ void output(){ System.out.println("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(){ System.out.println(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) { Generator<Input> gen = new RandomInputGenerator(); if(args.length == 1){ gen = new FileInputGenerator(args[0]); } run(gen); } } class RandomInputGenerator implements Generator<Input>{ public Input next(){ return Input.randomSelection(); } } 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()); } }
Category enum将不同类型的Input进行分组,因为可以使用categorize()方法为switch语句生成
恰当的Category实例。 使用EnumMap确保了查询时的效率和安全。
设计缺陷: 它要求 enum State实例访问的VendingMachine属性必须声明为static,这意味着,
只能有一个VendiingMachine实例(事实上够了,因为每台机,只有一个应该程序)。