简体   繁体   English

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

[英]Refactor multiple If' statements in Java-8

I need to validate mandatory fields in my class 我需要在课堂上验证必填字段

For example, 9 fields must not be null . 例如, 9字段不能为null

I need to check if they are all null but I am using multiple if statements for this now as below: 我需要检查它们是否都为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]);
}

Do we have a better approach to reduce this boilerplate code or any design pattern or any new feature from Java-8 which I can leverage? 我们是否有更好的方法来减少这个样板代码或任何设计模式或Java-8的任何新功能,我可以利用它?

All the Object.isNull might be replaced with Optional object and its methods. 所有Object.isNull可以替换为Optional对象及其方法。 Let's take example the line: 让我们举个例子:

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

Would be simplified to (and squeezed on 1 line remains readable): 将简化为(并在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

And for the String.isEmpty(s) check, you have to create Optional in this way: 对于String.isEmpty(s)检查,您必须以这种方式创建Optional

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

A short way would be to pass those Optional object into the map and use the index to iterate through them and perform an action. 一种简短的方法是将这些Optional对象传递到映射中,并使用索引迭代它们并执行操作。 int count is a number of checkings: 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]));
}

And the for-cycle might be simplified as well: 并且for-cycle也可以简化:

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

Basically, there are two ways here: 基本上,这里有两种方式:

  • As suggested by the comment, NonNull as offered by Project Lombok for example 正如评论所建议的那样,例如,Project Lombok提供的NonNull
  • Java bean validation Java bean验证

I would heavily recommend to look into bean validation: 我强烈建议查看bean验证:

Define your classes that carry information as beans. 定义以bean作为信息的类。 And then use the wide range of annotations to mark the corresponding fields. 然后使用各种注释来标记相应的字段。 And then use some existing framework to do the validation for you. 然后使用一些现有的框架为您进行验证。 You can even define your own annotations there, that run your own code. 您甚至可以在那里定义自己的注释,运行自己的代码。

You can use javax.validator and hibernate.validator with @NotNull annotation on each field (or whichever field you want) on your excess POJO class. 您可以在excess POJO类的每个字段(或您想要的任何字段)上使用带有@NotNull注释的javax.validatorhibernate.validator This combination provides an extensive pattern checking as well. 这种组合也提供了广泛的模式检查。

By this you don't have to do all the if checks explicitly. 通过这种方式,您不必明确地执行所有if检查。 You can get ride of not only null checks but also pattern matching checks which can get scattered all over your code. 您不仅可以获得空检查,还可以获得模式匹配检查,这些检查可能会分散在您的代码中。

Basically the initialisation and assignments should not set any field to null. 基本上,初始化和赋值不应将任何字段设置为null。

If this is unopportune (a field being really logically optional), the field should probably be an Optional<...> , assigned with an Optional.ofNullable(...) . 如果这是不合逻辑的(字段在逻辑上是可选的),则该字段可能应该是Optional<...> ,并使用Optional.ofNullable(...)分配。 This ensures that at usage the field is safely processed, but causes editing work of course. 这可确保在使用时安全地处理该字段,但当然会导致编辑工作。

Seeing the code now, here it seems that there is no easy refactoring. 现在看到代码,似乎没有简单的重构。

The code could be refactored; 代码可以重构; somewhere a mapping of features is missing. 在某处缺少功能映射。

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]);
    }
}

Or such. 或者这样。

As easy refactoring you could introduce two helper methods : 由于易于重构,您可以引入两个辅助方法:

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

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

And use them in this way : 并以这种方式使用它们:

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]))
                     // ...  

By checking the type of the object to test you could still go further. 通过检查要测试的对象的类型,您仍然可以进一步。 You would have a single helper method that will apply the processing according to the argument type : 您将拥有一个将根据参数类型应用处理的辅助方法:

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

A Java 8 stream way would inline the helper in a filter and map() operations and would collect the String result : 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(""));

Other solution would be like this: same as @Nikolas answer. 其他解决方案是这样的:与@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()]);
    }
}

A little bit complicated, but I have a good solution because it's generic and can be used with any objects: 有点复杂,但我有一个很好的解决方案,因为它是通用的,可以用于任何对象:

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));

This code will print: 此代码将打印:

excess date is null
    limit id is null

The first class Checker contains: 第一类Checker包含:

  • sourceFunction - for getting the object sourceFunction - 用于获取对象
  • values - for checking each field from object obtained from sourceFunction values - 用于检查从sourceFunction获取的对象中的每个字段
  • children - a list of Checker 孩子 - 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 ""; } } 

and CheckerValue class: 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);
    }
 }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM