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つしかありません).