相关知识点:《Effective Java》 第33条:用EnumMap代替序数索引
第33条中提到了水的三态转化的例子。实际工作中常见的OA审核,也是相似的场景。
public enum Status {
CANCEL,
NOT_PASS,
ONE,
TWO,
THREE,
PASS,
;
public enum Transition {
ONE_CANCEL(ONE, CANCEL),
TWO_CANCEL(TWO, CANCEL),
THREE_CANCEL(THREE, CANCEL),
ONE_NOT_PASS(ONE, NOT_PASS),
TWO_NOT_PASS(TWO, NOT_PASS),
THREE_NOT_PASS(THREE, NOT_PASS),
ONE_TWO(ONE, TWO),
TWO_THREE(TWO, THREE),
THREE_PASS(THREE, PASS),
;
private final Status from;
private final Status to;
Transition(Status from, Status to) {
this.from = from;
this.to = to;
}
}
private static final Map<Status, Map<Status, Transition>> m = new EnumMap<>(Status.class);
static {
for (Status status : Status.values())
m.put(status, new EnumMap<>(Status.class));
for (Transition trans : Transition.values())
m.get(trans.from).put(trans.to, trans);
}
public static Transition from(Status from, Status to) {
return m.get(from).get(to);
}
public static Boolean isTransitionable(Status from, Status to) {
Transition trans = from(from, to);
return trans != null;
}
}
m存储的数据如下:
审核时,一般会传入nextStatus,和currentStatus比较,以判断状态是否可转换,只需调用isTransitionable方法。
测试用例:
public class StatusTest {
@Test
public void testIsUpdatable() {
// cancel
Assert.assertFalse(Status.isTransitionable(Status.CANCEL, Status.ONE));
Assert.assertFalse(Status.isTransitionable(Status.CANCEL, Status.TWO));
Assert.assertFalse(Status.isTransitionable(Status.CANCEL, Status.THREE));
Assert.assertFalse(Status.isTransitionable(Status.CANCEL, Status.CANCEL));
Assert.assertFalse(Status.isTransitionable(Status.CANCEL, Status.NOT_PASS));
Assert.assertFalse(Status.isTransitionable(Status.CANCEL, Status.PASS));
// not pass
Assert.assertFalse(Status.isTransitionable(Status.NOT_PASS, Status.ONE));
Assert.assertFalse(Status.isTransitionable(Status.NOT_PASS, Status.TWO));
Assert.assertFalse(Status.isTransitionable(Status.NOT_PASS, Status.THREE));
Assert.assertFalse(Status.isTransitionable(Status.NOT_PASS, Status.CANCEL));
Assert.assertFalse(Status.isTransitionable(Status.NOT_PASS, Status.NOT_PASS));
Assert.assertFalse(Status.isTransitionable(Status.NOT_PASS, Status.PASS));
// one
Assert.assertFalse(Status.isTransitionable(Status.ONE, Status.ONE));
Assert.assertTrue(Status.isTransitionable(Status.ONE, Status.TWO));
Assert.assertFalse(Status.isTransitionable(Status.ONE, Status.THREE));
Assert.assertTrue(Status.isTransitionable(Status.ONE, Status.CANCEL));
Assert.assertTrue(Status.isTransitionable(Status.ONE, Status.NOT_PASS));
Assert.assertFalse(Status.isTransitionable(Status.ONE, Status.PASS));
// two
Assert.assertFalse(Status.isTransitionable(Status.TWO, Status.ONE));
Assert.assertFalse(Status.isTransitionable(Status.TWO, Status.TWO));
Assert.assertTrue(Status.isTransitionable(Status.TWO, Status.THREE));
Assert.assertTrue(Status.isTransitionable(Status.TWO, Status.CANCEL));
Assert.assertTrue(Status.isTransitionable(Status.TWO, Status.NOT_PASS));
Assert.assertFalse(Status.isTransitionable(Status.TWO, Status.PASS));
// three
Assert.assertFalse(Status.isTransitionable(Status.THREE, Status.ONE));
Assert.assertFalse(Status.isTransitionable(Status.THREE, Status.TWO));
Assert.assertFalse(Status.isTransitionable(Status.THREE, Status.THREE));
Assert.assertTrue(Status.isTransitionable(Status.THREE, Status.CANCEL));
Assert.assertTrue(Status.isTransitionable(Status.THREE, Status.NOT_PASS));
Assert.assertTrue(Status.isTransitionable(Status.THREE, Status.PASS));
// pass
Assert.assertFalse(Status.isTransitionable(Status.PASS, Status.ONE));
Assert.assertFalse(Status.isTransitionable(Status.PASS, Status.TWO));
Assert.assertFalse(Status.isTransitionable(Status.PASS, Status.THREE));
Assert.assertFalse(Status.isTransitionable(Status.PASS, Status.CANCEL));
Assert.assertFalse(Status.isTransitionable(Status.PASS, Status.NOT_PASS));
Assert.assertFalse(Status.isTransitionable(Status.PASS, Status.PASS));
}
}
如果不用EnumMap,一般想到的判断逻辑,可能是下面的这种:
public static Boolean isUpdate(Status from, Status to) {
switch (from) {
case ONE:
switch (to) {
case CANCEL:
case NO_PASS:
case TWO:
return true;
default:
return false;
}
case TWO:
switch (to) {
case CANCEL:
case NO_PASS:
case THREE:
return true;
default:
return false;
}
case THREE:
switch (to) {
case CANCEL:
case NO_PASS:
case PASS:
return true;
default:
return false;
}
default:
return false;
}
}
相比这种方法,使用EnumMap的方法代码更简洁。
一般需要把状态值写入数据库中,只需要对上面代码进行改造即可:
Status增加2个属性,一个value,一个desc,并增加一构造函数
CANCEL(1, "取消"),
Status.CANCEL.getValue()