1. 概述

在涉及状态流转类操作的业务中,通常都需要控制 操作状态 的对应关系,以控制数据在某些状态下可以进行哪些操作,哪些操作不被允许,如果是比较复杂的业务,可能会通过流程引擎控制,但如果是一般的状态控制,则可以通过状态转换和操作的对应关系去控制。

2. 需求

定义如下业务 状态 和全部可能的 操作

状态:输入、进行中、已终止、已完成
操作:开始订货、终止、删除、完成

当前状态下可进行的操作规则如下:

当前状态

可进行的操作

操作

操作后状态

输入

开始订货/删除

开始订货

进行中

输入

开始订货/删除

删除

已删除

进行中

完成/终止

完成

已完成

进行中

完成/终止

终止

已终止

已终止

开始订货

开始订货

进行中

已完成


-

-

表现在页面如下(可进行的操作是蓝色按钮,不可进行的操作则置灰):

java 用枚举代替if 枚举替换if_设计规范

3. 分析

一个操作可以有多个可操作状态,只有当数据处于可操作状态下时才可进行该操作
一个操作只会有一个结果状态

4. 实现

我们可以定义两个枚举,状态枚举(StatusEnum)操作枚举(OperationEnum),在操作枚举中包含结果状态和可操作状态,由于可操作性状态是多个,则用可变参数(数组)表示(其他类型参数亦可)。通过定义可操作性状态,我们可以很容易控制某个操作只有在这些特定的状态下才可操作,而无需通过if-else逻辑控制。

状态枚举(StatusEnum)

@NoArgsConstructor
@AllArgsConstructor
public enum StatusEnum {
    INIT(0, "输入"),
    EXECUTING(1, "进行中"),
    ABORT(2, "已终止"),
    FINISHED(3, "已完成");

    @Getter
    private Integer code;

    @Getter
    private String desc;

    public static String getDescByCode(Integer code) {
        return Arrays.stream(StatusEnum.values())
                .filter(e -> e.getCode().equals(code))
                .map(StatusEnum::getDesc)
                .findFirst()
                .orElse(null);
    }
}

操作枚举(OperationEnum)
在操作枚举中定义结果状态 StatusEnum resultStatus 和 可操作状态 StatusEnum[] availableStatus。例如,只有在数据处于EXECUTING(1, "进行中") 时才可进行 FINISH(2, "完成") 操作。

@NoArgsConstructor
public enum OperationEnum {
    START_ORDER(1, "开始订货", StatusEnum.EXECUTING, StatusEnum.INIT, StatusEnum.ABORT),
    FINISH(2, "完成", StatusEnum.FINISHED, StatusEnum.EXECUTING),
    STOP(3, "终止", StatusEnum.ABORT, StatusEnum.EXECUTING),
    DELETE(4, "删除", null, StatusEnum.INIT);

    @Getter
    private Integer code;

    @Getter
    private String desc;

    @Getter
    private StatusEnum resultStatus;

    @Getter
    private StatusEnum[] availableStatus;

    OperationEnum(Integer code, String desc, StatusEnum resultStatus, StatusEnum... availableStatus) {
        this.code = code;
        this.desc = desc;
        this.resultStatus = resultStatus;
        this.availableStatus = availableStatus;
    }

    /**
     * 获取全部操作 code 集合
     * 
     * @return 全部操作 code 集合
     */
    public static List<Integer> getOperationCodeList() {
        return Arrays.stream(OperationEnum.values()).map(OperationEnum::getCode).collect(Collectors.toList());
    }

    /**
     * 获取某项操作的结果状态
     * 
     * @param code 操作code
     * @return 结果状态
     */
    public static Integer getResultStatusCodeByCode(Integer code) {
        for (OperationEnum operationEnum : OperationEnum.values()) {
            if (operationEnum.getCode().equals(code)) {
                StatusEnum resultStatus = operationEnum.getResultStatus();
                if (resultStatus != null) {
                    return resultStatus.getCode();
                }
            }
        }
        return null;
    }

    /**
     * 获取某项操作的可操作状态
     * 
     * @param code 操作code
     * @return 可操作状态code集合
     */
    public static List<Integer> getAvailableStatusCodeList(Integer code) {
        for (OperationEnum operationEnum : OperationEnum.values()) {
            if (operationEnum.getCode().equals(code)) {
                StatusEnum[] availableStatus = operationEnum.getAvailableStatus();
                return Arrays.stream(availableStatus).map(StatusEnum::getCode).collect(Collectors.toList());
            }
        }
        return new ArrayList<>();
    }
}

有了上述操作枚举及对应状态枚举的关系,可以定义获取某项操作的可操作状态的方法getAvailableStatusCodeList(Integer),进而在接口中判断当前操作是否可执行,如果可以执行,则可通过获取结果状态方法getResultStatusCodeByCode(Integer)设置操作的结果状态。