枚举实现优雅的验证


一、模式结构

1. 背景介绍

枚举的验证模式核心是基于接口实现而达到,当一个枚举类实现接口后,其每个元素都将需要实现接口类中定义的接口方法。基于此特性,我们即可将一个枚举定义为一类对象,而内部枚举成员则为对象的成员属性,同时由于接口实现的特性,枚举中的每个元素则需要接口类中定义的接口方法。

举个易懂的例子,程序中存在 AppleOrange 两种水果,二者都包含 MEATSHAPE 两种属性,为了实现验证该属性的合法性,定义接口 Element 包含对于属性的描述方法,如名称与是否为空等等。当 Apple 枚举实现于 Element 接口,则枚举中的每个元素均需实现 Element 接口方法,即可在元素方法实现中定义验证规则。

二、代码实现

1. 接口定义

创建接口类 Element 并定义对应所需字段。

public interface Element {

    String key();

    String name();

    boolean required();

    default Object defaultValue() {
        return null;
    };
}

2. 枚举实现

(1) 实现一

创建枚举 AppleElement 实现于 Element 接口,此时枚举中的元素成员需实现对应接口。

public enum AppleElement implements Element {
    SHAPE {
        @Override
        public String key() {
            return "shape";
        }

        @Override
        public String name() {
            return "Apple shape";
        }

        @Override
        public boolean required() {
            return true;
        }
    },
    MEAT {
        @Override
        public String key() {
            return "meat";
        }
        
        @Override
        public String name() {
            return "Apple meat";
        }

        @Override
        public boolean required() {
            return true;
        }
    };

    public static AppleElement get(int index) {
        for (AppleElement value : AppleElement.values()) {
            if (value.ordinal() == index) {
                return value;
            }
        }
        throw new IllegalArgumentException("Not found");
    }
}
(2) 实现二

同理再创建一个枚举 OrangeElement,定义方式同上。

public enum OrangeElement implements Element {
    SHAPE {
        @Override
        public String key() {
            return "shape";
        }

        @Override
        public String name() {
            return "Orange shape";
        }

        @Override
        public boolean required() {
            return true;
        }
    },
    MEAT {
        @Override
        public String key() {
            return "meat";
        }

        @Override
        public String name() {
            return "Orange meat";
        }

        @Override
        public boolean required() {
            return true;
        }
    };

    public static OrangeElement get(int index) {
        for (OrangeElement value : OrangeElement.values()) {
            if (value.ordinal() == index) {
                return value;
            }
        }
        throw new IllegalArgumentException("Not found");
    }
}

3. 验证示例

完成上述步骤后即可快速实现同源但不同类型的数据验证,相应示例如下:

public class ElementTest {

    @Test
    public void demo() throws Exception {
        // 定义对象用于描述 Apple
        var appleData = List.of(
                Map.of(AppleElement.SHAPE.ordinal(), "round"),
                Map.of(AppleElement.MEAT.ordinal(), "red")
        );
        // 定义对象用于描述 Orange
        var orangeData = List.of(
                Map.of(OrangeElement.SHAPE.ordinal(), "round"),
                Map.of(OrangeElement.MEAT.ordinal(), "white")
        );
        // 验证描述信息是否符合规范
        validate(appleData, AppleElement.class);
        validate(orangeData, OrangeElement.class);

    }

    private void validate(List<Map<Integer, String>> dataList,
                          Class<? extends Element> aClass) throws Exception {
        for (Map<Integer, String> data : dataList) {
            var offset = 0;
            var msg = new ArrayList<>();
            for (Map.Entry<Integer, String> row : data.entrySet()) {
                offset++;
                var index = row.getKey();
                var value = row.getValue();
                // 通过反射获取枚举对应元素
                Element element = (Element) aClass
                        .getMethod("get", int.class)
                        .invoke(null, index);
                boolean blank = Objects.isNull(value) || Objects.equals("", value);
                // 此处为验证是否为空
                // 可根据业务实现更复杂逻辑
                if (element.required() && blank) {
                    msg.add("Filed {" + element.key() + "} can't be null");
                }
            }
            if (!msg.isEmpty()) {
                // 若非法则输入信息
                System.out.println(offset + ": " + msg);
            }
        }
    }
}

文章作者: 烽火戏诸诸诸侯
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 烽火戏诸诸诸侯 !
  目录