在Java-8中重构多个If'语句

时间:2018-08-17 08:16:04

标签: java arrays java-8 optional

我需要验证班级中的必填字段

例如,9字段不得为null

我需要检查它们是否都为空,但是我现在正在使用多个if语句,如下所示:

StringBuilder mandatoryExcessFields = new StringBuilder(MANDATORY_EXCESS_FIELDS.length);

if(Objects.isNull(excess.getAsOfDate())){
    mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[0]);
}

if(StringUtils.isEmpty(excess.getStatus())) {
    mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[1]);
}

if(Objects.isNull(excess.getLimit())) {
    mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[2]);
}

if(!Objects.isNull(excess.getLimit()) && Objects.isNull(excess.getLimit().getId())) {
    mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[3]);
}

if(!Objects.isNull(excess.getLimit()) && Objects.isNull(excess.getLimit().getAsOfDate())) {
    mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[4]);
}

if(Objects.isNull(excess.getExposure())) {
    mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[5]);
}

if(!Objects.isNull(excess.getExposure()) && Objects.isNull(excess.getExposure().getCoordinates())) {
    mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[6]);
}

if(!Objects.isNull(excess.getExposure()) && Objects.isNull(excess.getExposure().getValue())) {
    mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[7]);
}

if(StringUtils.isEmpty(excess.getLimitValue())) {
    mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[8]);
}

我们是否有更好的方法来减少这种样板代码,我可以利用的Java-8的任何设计模式或任何新功能?

7 个答案:

答案 0 :(得分:3)

所有Object.isNull都可以替换为Optional对象及其方法。让我们以以下行为例:

if (!Objects.isNull(excess.getLimit()) && Objects.isNull(excess.getLimit().getId())) {
    mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[3]);
}

将简化为(并压缩在1行以保持可读性):

Optional.ofNullable(excess.getLimit())                                // check the Limit
        .map(limit -> limit.getId())                                  // if not null, getId
        .ifPresent(i -> builder.append(MANDATORY_EXCESS_FIELDS[3]));  // Append if present

对于String.isEmpty(s)检查,您必须以这种方式创建Optional

Optional.ofNullable(excess.getStatus()).filter(s -> !StringUtils.isEmpty(s))

一种简短的方法是将那些Optional对象传递到地图中,并使用索引来遍历它们并执行操作。 int count是许多检查项:

Map<Integer, Optional<?>> map = new HashMap<>();
map.put(...);
map.put(1, Optional.ofNullable(excess.getStatus()).filter(s -> !StringUtils.isEmpty(s)));
map.put(...);
map.put(3, Optional.ofNullable(excess.getLimit()).map(limit -> limit.getId()));
map.put(...);

for (int index=0; index<count; index++) {
    map.get(index).ifPresent(any -> mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[index]));
}

for循环也可以简化:

IntStream.range(0, count).forEach(index -> 
    map.get(index)
       .ifPresent(any -> mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[index])));

答案 1 :(得分:2)

基本上,这里有两种方法:

  • 如评论所建议,NonNull例如由龙目岛计划提供
  • Java bean验证

我强烈建议您研究bean验证:

定义将信息作为bean承载的类。然后使用各种注释标记相应的字段。然后使用一些现有框架为您进行验证。您甚至可以在那里定义自己的自己批注,这些批注运行您自己的自己的代码。

答案 2 :(得分:2)

您可以在javax.validator POJO类的每个字段(或所需的任何字段)上使用带有hibernate.validator批注的@NotNullexcess。这种组合还提供了广泛的模式检查。

通过这种方法,您不必显式地执行所有if检查。您不仅可以使用null检查,还可以使用模式匹配检查,这些检查可能分散在您的所有代码中。

答案 3 :(得分:1)

基本上,初始化和赋值不应将任何字段设置为null。

如果这是不合时宜的(一个字段在逻辑上实际上是可选的),则该字段可能应该是一个Optional<...>,并分配了一个Optional.ofNullable(...)。这样可以确保在使用时可以安全地处理该字段,但是当然会导致编辑工作。

现在看代码,这里似乎没有简单的重构。

代码可以重构;在某些地方缺少功能映射。

Predicate<Excess>[] parts = {
    exc -> Objects.isNull(exc.getAsOfDate()),
    exc -> StringUtils.isEmpty(exc.getStatus()),
    ...
};
for (int i = 0; i < parts.length; ++i) {
    if (parts[i].test(excess)) {
        mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[i]);
    }
}

等等。

答案 4 :(得分:1)

为方便重构,您可以引入两种帮助方法:

private String createErrorMsgIfObjectNull(Object o, String errorMessage) {
    return Objects.isNull(o) ?  errorMessage : "";
}

private String createErrorMsgIfStringEmpty(String s, String errorMessage) {
    return StringUtils.isEmpty(s) ?  errorMessage : "";
}

并以这种方式使用它们:

StringBuilder mandatoryExcessFields = new StringBuilder(MANDATORY_EXCESS_FIELDS.length);

mandatoryExcessFields.append(createErrorMsgIfObjectNull(excess.getAsOfDate(), MANDATORY_EXCESS_FIELDS[0]))
                     .append(createErrorMsgIfStringEmpty(excess.getStatus(), MANDATORY_EXCESS_FIELDS[1]))
                     .append(createErrorMsgIfObjectNull(excess.getLimit(), MANDATORY_EXCESS_FIELDS[2]))
                     // ...  

通过检查要测试的对象的类型,您仍然可以走得更远。您将只有一个帮助程序方法,该方法将根据参数类型进行处理:

private String createErrorMsgIfNullOrEmptyString(Object o, String errorMessage) {
    if (o instanceof String) {
        return StringUtils.isEmpty((String)o) ? errorMessage : "";
    }
    return Objects.isNull(o) ? errorMessage : "";
}

Java 8流方法将在filtermap()操作中内联帮助程序,并会收集String结果:

List<SimpleImmutableEntry<Object, String>> objectAndErrorMessageList = new ArrayList<>();
int i = 0;
objectAndErrorMessageList.add(new SimpleImmutableEntry<>(excess.getAsOfDate(), MANDATORY_EXCESS_FIELDS[i++]));
objectAndErrorMessageList.add(new SimpleImmutableEntry<>(excess.getStatus(), MANDATORY_EXCESS_FIELDS[i++]));
// and so for

String globalErrorMsg = 
    objectAndErrorMessageList.stream()
                             .filter(e -> {
                                 Object objectToValid = e.getKey();
                                 if (objectToValid == null) {
                                     return true;
                                 }
                                 if (objectToValid instanceof String && StringUtils.isEmpty(objectToValid)) {
                                     return true;
                                 }
                                 return false;
                             })
                             .map(SimpleImmutableEntry::getValue)
                             .collect(Collectors.joining(""));

答案 5 :(得分:0)

其他解决方案如下:与@Nikolas答案相同。

Map<Integer, Predicate<Excess>> map = new HashMap<>();

Predicate<Excess> checkStatus = excess -> excess.getStatus().isEmpty();
Predicate<Excess> checkLimit = excess -> Objects.isNull(excess.getLimit());
Predicate<Excess> checkLimitId = excess -> Objects.isNull(excess.getLimit().getId());
Predicate<Excess> checkLimitAndId = checkLimit.and(checkLimitId);
// other predicates 

map.put(1,checkStatus);
map.put(2,checkLimit);
map.put(3,checkLimitAndId);
// put other predicates ...


for (Map.Entry<Integer, Predicate<Excess>> m : map.entrySet()) {
    if (m.getValue().test(excess)) {
        mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[m.getKey()]);
    }
}

答案 6 :(得分:0)

有点复杂,但是我有一个很好的解决方案,因为它是通用的,可以与任何对象一起使用:

Excess excess = new Excess(new Limit());

Checker<Excess, Excess> checker = new Checker<>(
    identity(),
    List.of(
        new CheckerValue<>("excess date is null", Excess::getAsOfDate),
        new CheckerValue<>("limit is null", Excess::getLimit)
    ),
    List.of(new Checker<>(Excess::getLimit, List.of(new CheckerValue<>("limit id is null", Limit::getId))))
);

System.out.println(checker.validate(excess));

此代码将打印:

excess date is null
    limit id is null

第一堂课Checker包含:

  • sourceFunction-用于获取对象
  • 值-用于检查从sourceFunction获得的对象中的每个字段
  • 孩子-Checker

    的列表
    class Checker<S, T> {
    
    Function<S, T> sourceFunction;
    List<CheckerValue<T>> values;
    List<Checker<T, ?>> children = emptyList();
    
    /*All args constructor; 2 args constructor*/
    
    public String validate(S object) {
        T value = sourceFunction.apply(object);
        if(value != null) {
            String valueString = values.stream().map(v -> v.validate(value)).filter(Optional::isPresent).map(Optional::get).collect(joining("\n"));
            valueString += "\n\t";
            valueString += children.stream().map(c -> c.validate(value)).collect(Collectors.joining("\n"));
            return valueString;
        }
        return "";
    }
    }
    

CheckerValue类:

class CheckerValue<T> {

    String validationString;
    Function<T, Object> fun;

    /*all args constructor*/

    public Optional<String> validate(T object) {
        return fun.apply(object) != null ? Optional.empty() : Optional.of(validationString);
    }
 }