相關知識點:《Effective Java》 第33條:用EnumMap代替序數索引
第33條中提到了水的三態(tài)轉化的例子。實際工作中常見的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存儲的數據如下:

image.png
審核時,一般會傳入nextStatus,和currentStatus比較,以判斷狀態(tài)是否可轉換,只需調用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的方法代碼更簡潔。
一般需要把狀態(tài)值寫入數據庫中,只需要對上面代碼進行改造即可:
Status增加2個屬性,一個value,一個desc,并增加一構造函數
CANCEL(1, "取消"),
Status.CANCEL.getValue()