JAVA枚举类状态机 与 Java枚举策略模式

时间:2021-01-23 21:57:12

写的比较晚了,贴代码供参考


1、普通的enum方法设计如下,缺点是不方便维护,因为是switch语句,容易在扩展的时候忘了改相关的代码。但如果你的代码千年不变,这个缺点就不明显。

注意:测试结果参见第2点。

package enumclass;

public enum PayrollBySwitch {
Monday,
Tuesday,
Wendsday,
Thursday,
Friday,
Saturday,
Sunday;

private static final int FIXED_WORK_HOUR_PER_DAY = 8;

//加班工资会是基本工资的一半。
private static final double OVER_TIME_PAY_RATE = 1/2;

public double pay(double workHours, double payPerHour){

double basePay = workHours * payPerHour;

double overTimePay = 0;
switch(this){
//周六日每个小时都算加班工资
case Saturday: case Sunday:
overTimePay = workHours * payPerHour * OVER_TIME_PAY_RATE;
break;
default:
overTimePay = workHours <= FIXED_WORK_HOUR_PER_DAY ?
0 : workHours - FIXED_WORK_HOUR_PER_DAY;
break;

}
return basePay+overTimePay;
}
}

2、enum方法的改进版,使用了enum strategy模式。要点:

1、设计一个private的enum,作为strategy的抽象。

2、顶层enum的构造器需要传入策略PayType。

3、策略类需要定义一个抽象类,让private enum去实现这个方法。这里用到enum的另一个特性:特定于常量的方法(Constant-specific class body的Constant -specific method implementation)。

package enumclass;

public enum PayrollByEnumStrategy {
Monday(PayType.WeekDay),
Tuesday(PayType.WeekDay),
Wendsday(PayType.WeekDay),
Thursday(PayType.WeekDay),
Friday(PayType.WeekDay),
Saturday(PayType.WeekEnd),
Sunday(PayType.WeekEnd);

PayType payType;
private PayrollByEnumStrategy(PayType payType) {
this.payType = payType;
}

public double pay(double workHours, double payPerHour){
return payType.pay(workHours, payPerHour);
}



private enum PayType{

WeekDay{

@Override
double overTimePay(double workHours, double payPerHour) {
double overTimePay = workHours <= FIXED_WORK_HOUR_PER_DAY ?
0 : (workHours - FIXED_WORK_HOUR_PER_DAY)*payPerHour*OVER_TIME_PAY_RATE;
return overTimePay;
}

},
WeekEnd{

@Override
double overTimePay(double workHours, double payPerHour) {
double overTimePay = workHours * payPerHour * OVER_TIME_PAY_RATE;
return overTimePay;
}

};
private static final int FIXED_WORK_HOUR_PER_DAY = 8;

//加班工资会是基本工资的一半。
private static final double OVER_TIME_PAY_RATE = 1/2;


abstract double overTimePay(double workHours, double payPerHour);

public double pay(double workHours, double payPerHour){

double basePay = workHours * payPerHour;

double overTimePay = overTimePay(workHours,payPerHour);
return basePay+overTimePay;
}

}
}

1 和 2的测试结果:

工资总额 by Switch= ¥3510.0
工资总额 by Enum Strategy= ¥3510.0

测试类如下:

package enumclass;

public class WorkPayment {

public static void main(String[] args) {
// TODO Auto-generated method stub
//计算一周的工资总额——每天工作10小时,每小时400块。
double sum = 0;
for(PayrollBySwitch day : PayrollBySwitch.values()){
sum += day.pay(10, 50);
}

System.out.println("工资总额 by Switch= ¥"+sum);


sum = 0;
for(PayrollBySwitch day : PayrollBySwitch.values()){
sum += day.pay(10, 50);
}

System.out.println("工资总额 by Enum Strategy= ¥"+sum);
}

}


3、覆盖enum的toString带来的方便。

如下面例子,打印日志时,很方便

package enumclass;

//enum strategy with toString overrided
public enum Operation {
PLUS("*") { double eval(double x, double y) { return x + y; } },
MINUS("-") { double eval(double x, double y) { return x - y; } },
TIMES("*") { double eval(double x, double y) { return x * y; } },
DIVIDE ("/"){ double eval(double x, double y) { return x / y; } };

String symbol = null;
Operation(String symbol){
this.symbol = symbol;
}

public String toString(){
return symbol;
}

// Do arithmetic op represented by this constant
abstract double eval(double x, double y);

}

测试类
package enumclass;

public class OperationTest {


public static void main(String[] args) {
double x = 2.000;
double y = 4.0000;

for(Operation oper : Operation.values()){
System.out.printf("%f %s %f = %f%n",x,oper,y,oper.eval(x, y));
}
}

}

测试结果如下:

2.000000 * 4.000000 = 6.000000
2.000000 - 4.000000 = -2.000000
2.000000 * 4.000000 = 8.000000
2.000000 / 4.000000 = 0.500000


4、最后是状态机的实现,已上传。

要点:

1、enum可以实现interface。

2、状态机维护需要两种对象:Context上下文、States状态

3、Context管理State。

4、用while运行状态机,其实这个while放到Context内更好,应该是状态机自己维护状态,而不是外部通过状态机去控制状态。

5、比较巧的设计是,每个state的操作返回boolean,作为状态是否终止的判断。

资源:http://download.csdn.net/detail/xzongyuan/9871848

State设置为如下时:

context.state(States.WHITE);

运行结果:

Current State = WHITE
Current State = BLUE


package enumstatemachin;

public class InfiniteMachineTest {

public static void main(String[] args) {

Context context = new ContextImp();
context.state(States.WHITE);
while(context.state().process(context));
}
}
package enumstatemachin;import java.nio.ByteBuffer;enum States implements State {    RED {        public boolean process(Context context) {            context.state(States.GREEN);            System.out.println("Current State = " + this);            return true;        }    }, GREEN {        public boolean process(Context context) {            context.state(States.BLACK);            System.out.println("Current State = " + this);            return true;        }    },BLACK {        public boolean process(Context context) {            context.state(States.YELLOW);            System.out.println("Current State = " + this);            return true;        }    },YELLOW {        public boolean process(Context context) {            context.state(States.WHITE);            System.out.println("Current State = " + this);            return true;        }    },WHITE {        public boolean process(Context context) {            context.state(States.BLUE);            System.out.println("Current State = " + this);            return true;        }    },BLUE{    public boolean process(Context context) {    context.state(States.RED);            System.out.println("Current State = " + this);return false;    }      };public abstract boolean process(Context context);}

package enumstatemachin;

interface State {
/**
* @return true to keep processing, false to read more data.
*/
boolean process(Context context);
}
package enumstatemachin;import java.nio.ByteBuffer;public interface Context {    State state();    void state(State state);}

package enumstatemachin;

import java.nio.ByteBuffer;

public class ContextImp implements Context {


State state;

@Override
public State state() {
// TODO Auto-generated method stub
return state;
}

@Override
public void state(State state) {
this.state = state;

}

}