如何优化if判断中,极其长的条件?

目前需要写一个判断完整度的方法
A实体类共有20个字段,其中12个字段参与计算,计作:a1到a12
B实体类共有16个字段,其中8个字段参与计算,计作:b1到b8
C实体类共有19个字段,其中10个字段参与计算,计作:c1到c10
所有30个字段都不为空才算完整

目前的判断语句为:

if (!StringUtils.isEmpty(a1) && ...
    && !StringUtils.isEmpty(c10)){
    return true;
}

目前的代码很长,是否可以简单优化?为什要那样优化?

阅读 14.2k
13 个回答

看题主的问题,虽然直接每个对象每个字段都去get判空是可以,但是这样的代码明显不方便维护,而且可能越加越多,因此我分析主要需要解决以下两个问题

  1. 每个实体类中的字段并不是所有字段都需要判空,所以这里会有修改的隐藏需求(增加或减少需要判断的字段)
  2. 如果解决第一步,能将需要校验的字段筛选出来,那剩下的问题就是如何将所有的字段值取出来加以判断

针对这两个问题,我认为

  1. 问题1可以通过自定义注解来标示一下需要校验的字段,用注解标示字段后,在需要增加或减少字段校验,只需要修改对应实体类里的属性注解即可,不需要再修改校验类
  2. 问题2,取值出来,自然想到是反射了

这就有了一下的代码:

首先是标注的注解EmptyCheck

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EmptyCheck {
}

然后注解到一个实体类A中的字段里,其中a1,a3字段需要判空,a2字段不需要

@Getter
@Setter
public class A {

    @EmptyCheck
    private String a1;

    private String a2;

    @EmptyCheck
    private String a3;
}

以上是准备工作,接下来就是写如何校验了,我采用了一个校验类EmptyCheckUtils,把所有校验方法放到里面了
既然是对于A,B之类的实体类对象进行校验,那第一个提供的方法,就是针对所有的实体类对象进行的校验isAllNotEmpty,判断所有对象的所有有EmptyCheck标注的字段值是否都不为空

/**
     * 判断所有对象的所有有EmptyCheck标注的字段值是否都不为空
     * @param objects
     * @return
     */
    public static Boolean isAllNotEmpty(Object ... objects){
        return Arrays.stream(objects).allMatch(EmptyCheckUtils::isNotEmpty);
    }

接下来就是单个对象的判断方法isNotEmpty

/**
     * 判断一个对象的所有有EmptyCheck标注的字段值是否都不为空
     * @param object
     * @return
     */
    public static Boolean isNotEmpty(Object object){
        Class tClass = object.getClass();
                // 获取该类的所有字段
        return EmptyCheckUtils.getAllField(tClass)
                              // 过滤掉没有标注EmptyCheck注解的字段
                              .filter(EmptyCheckUtils::isEmptyCheckField)
                              // 获取字段的值
                              .map(field -> EmptyCheckUtils.getValue(object, field))
                              // 判断值是否为空
                              .allMatch(EmptyCheckUtils::isObjectNotEmpty);
    }

最后就是其他的辅助方法了

   /**
     * 根据目标class类获取该类下的所有字段(除静态属性字段)
     * @param tClass
     * @return
     */
    private static Stream<Field> getAllField(Class tClass) {
        Field[] dFields = tClass.getDeclaredFields();
        return Arrays.stream(dFields)
                      // 去掉静态属性
                     .filter(field -> !Modifier.isStatic(field.getModifiers()));
    }
    
    /**
     * 判断该字段field是否有注解EmptyCheck
     * @param field
     * @return
     */
    private static Boolean isEmptyCheckField(Field field){
        EmptyCheck annotation = field.getAnnotation(EmptyCheck.class);
        return Objects.nonNull(annotation);
    }

    /**
     * 根据字段field和对象object,获取该对象上field的值(基于get方法获取)
     * @param object
     * @param field
     * @return
     */
    public static Object getValue(Object object, Field field){
        Object value = null;
        String fieldName = field.getName();
        try {
            Method method = object.getClass().getMethod(UtilsConstants.GETTER + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1, fieldName.length()));
            value = method.invoke(object);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return value;
    }

   /**
     * 判断该值是否为空,可以根据该值对象去定制化判空方式
     * @param object
     * @return
     */
    public static Boolean isObjectNotEmpty(Object object){
        // 各种类型的判空
        // 这里只是简单的用了一个Null判空
        return Objects.nonNull(object);
    }

测试类:

public static void main(String[] args) {
        A a = new A();
        System.out.println(EmptyCheckUtils.isAllNotEmpty(a));
        a.setA1("1");
        System.out.println(EmptyCheckUtils.isAllNotEmpty(a));
        a.setA2("2");
        System.out.println(EmptyCheckUtils.isAllNotEmpty(a));
        a.setA3("3");
        System.out.println(EmptyCheckUtils.isAllNotEmpty(a));
    }
   

运行结果

clipboard.png

结果可以看到,由于是a2属性不需要判空,所有只有等a3设置完值后,判断结果才会为true

最后说明:为了方便展示,才将业务校验类EmptyCheckUtils中填充了很多和业务无关的校验方法,因此辅助方法中除了和业务有关的isObjectNotEmpty方法,其他的getValueisEmptyCheckFieldgetAllField等方法建议拆分到其他工具类中,保持业务方法类和其他辅助工具方法类的健壮稳定性哈

String[] strings = {"a1",null,"a3","c1","c3","d1", "d2","d3"};
boolean flag = Arrays.stream(strings).anyMatch(StringUtils::isEmpty);

熟悉流处理 很多代码都可以简化

把判断条件抽离,归类。
通过函数语义式的调用返回结果来判断,好修改,也好阅读。
比如if (validationA() && validationB())

可以尝试一下正则表达式

[StringUtils].every(function(StringUtil, index){
    return !StringUtil.isEmpty(a1)
})

原生的数组方法 只有全真才输出true

维护一个需要验证的字段名数组,for...in循环遍历对象属性+一个flag=true,如果是需要遍历的则isEmpty验证,如果不是continue。出现空了flag=false就break(如果三个实体需要校验的字段名没有重复,把三个对象union一份出来单独做校验)

if(m_state==1)
{
switch(m_tSpeed)
{
case 1:
....
}

}
else if(m_state==2)
{
同上
}

大概意思就是这样,可以把一个判断提出来先做,然后再做另一个判断!

自己编写一些辅助方法即可,将所有判断的字符串放到一个数组或 List 中,判断全部非空即可。

static boolean isEmpty(String s) {
    return (s == null) || s.isEmpty();
}

static boolean isEmptyAll(String[] arr) {
    boolean res = (arr != null) && (arr.length > 0);
    if (res) {
        for (String s : arr) {
            res &= isEmpty(s);
        }
    }
    return res;
}

static boolean isEmptyAll(Collection<String> list) {
    boolean res = (list != null) && !list.isEmpty();
    if (res) {
        for (String s : list) {
            res &= isEmpty(s);
        }
    }
    return res;
}

static boolean isNonEmpty(String s) {
    return (s != null) && !s.isEmpty();
}

static boolean isNonEmptyAll(String[] arr) {
    boolean res = (arr != null) && (arr.length > 0);
    if (res) {
        for (String s : arr) {
            res &= isNonEmpty(s);
        }
    }
    return res;
}

static boolean isNonEmptyAll(Collection<String> list) {
    boolean res = (list != null) && !list.isEmpty();
    if (res) {
        for (String s : list) {
            res &= isNonEmpty(s);
        }
    }
    return res;
}

通过反射,或者BeanMap即可以字符串形式用循环来遍历对象上的属性了

可以用断言assert , Assert.noNullElements(new Object[]{1,"222"},"所有字段都不能为空"); 三个类 每个类中各加一个方法判断自己的字段,调用这三个方法,或者 写三次Assert.noNullElements(new Object[]{1,"222"},"所有字段都不能为空");分别传入各个类的字段。

新手上路,请多包涵

编写一个新函数?

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题