Enumの自動販売機をご利用ください

5397 ワード

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()メソッドを定義しました.しかしながら、その2つの特殊な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は、categorize()メソッドを使用してswitch文を生成できるため、異なるタイプのInputをグループ化します.
適切なCategoryの例.EnumMapを使用すると、クエリの効率性とセキュリティが確保されます.
 
 
設計上の欠陥:enum StateインスタンスにアクセスするVendingMachineプロパティをstaticとして宣言する必要があります.これは、
VendiingMachineインスタンスは1つしかありません(実際には十分です.各マシンにはプログラムが1つしかありません).