Java 8避免大量if/else语句

5

我有一个类似这样的东西:

 public boolean isValidObject(SomeObject obj){
    if(obj.getField() == null){
      LOG.error("error code 1");
      return false;
    }
    if(obj.getField().getSize() > 500){
      LOG.error("error code 2");
      return false;
    }
    ......
    if(someCondition()){
     log something
     return false;
    }

    return true;
}

这个使用Java 8中的Lambda表达式最简洁明了,你需要这样写:

9
为什么要使用lambda? - Reimeus
你可以使用Lambda表达式,但我怀疑这会使代码更难理解。 - Peter Lawrey
我同意Reimeus和Peter的观点。这会使代码可读性降低。 - Zach
我不想使用lambda,我想要摆脱50个if/else语句。 - Mihai
那么你实际上是在做什么呢?看起来像是Bean验证,有很多库和方法可以帮助减少这种复杂性。 - Makoto
1
提示:Bean Validation - Basil Bourque
6个回答

14

使用多态来完成此任务。为每个逻辑验证器创建一个类,并在列表中链接它们。以下是一个非常好的答案,其中包含您需要的信息:https://dev59.com/-WAg5IYBdhLWcg3wlb1h#23501390

public interface Validator<SomeObject>{
    public Result validate(SomeObject object);
}

实现:

public class SomeFieldSizeValidator implements Validator<SomeObject> {

@Override
public Result validate(SomeObject obj) {
    // or you can return boolean true/false here if it's enough
    return obj.getField().getSize() > 500 ? Result.OK : Result.FAILED;
    }
}

调用验证链:

List<Validator> validators = ... create ArrayList of needed Validators
for (Validator v : validators) {
if (!v.validate(object)) { 
  ... throw exception, you know validator and object here
}

1
这实际上是正确的解决方案(或者使用一个做同样事情的库)。我不知道为什么它被投票下降。通过添加一个特定于OP问题的Validator示例实现以及如何调用它,可以改进它。无论如何,我给它加一分。 - nhouser9
1
我没有点踩,但是如果你按代码行数计费的话,把每个条件都转换成一个新类是很好的做法,但是把一个方法分成50个类真的是一个好主意吗? - Peter Lawrey
2
没有人为代码行数付费。通过使用模式,你可以创建可读的代码。作者说:“我想摆脱50个if/else语句。” - 这样做很好地完成了任务。 - Gondy
有趣的是,两个人有同样的想法。如果我有一个真正的键盘,我的答案就会第一个出现 :-) 并且加1是为了我留下的代码示例。 - GhostCat
1
顺便提一下:由于Validator是一个单抽象方法类,因此验证器可以作为lambda表达式编写,从而减少代码行数。例如:Validator v = obj -> obj.getField().getSize() > 500 ? Result.OK : Result.FAILED; - Thomas Fritsch
显示剩余2条评论

4

我可能会返回错误,但这仍然需要使用一些 if 语句。

public String isValidObject(SomeObject obj){
    if (obj.getField() == null) return "error code 1";
    if (obj.getField().getSize() > 500) return "error code 2";
    ......
    if (someCondition()) return "something";
    return OK;
}

您可以通过单元测试此方法,以查看它是否针对不同的无效对象返回所期望的错误。

我想摆脱50个if/else语句。

如果您有50个条件,它们都具有不同的结果,那么您需要进行50次检查。您可以按照以下方式更改结构。

static final Map<Predicate<SomeObject>, String> checks = new LinkedHashMap<>();
static {
    checks.put((Predicate<SomeObject>) o -> o.getField() == null, "error code 1");
    checks.put((Predicate<SomeObject>) o -> o.getField().getSize() > 500, "error code 2");
}

public String isValidObject(SomeObject obj) {
    for (Predicate<SomeObject> test : checks.keySet())
        if (test.test(object))
            return checks.get(test);
    return OK;
}

然而,就个人而言,这并不更清晰,反而会更难调试,例如断点。


2
你用lambda很好地解决了这个问题,但是这段代码的逻辑点是什么?我看到一些验证。如果你的验证变得更加复杂怎么办?祝你调试愉快...我不会在真实项目中使用这段代码。 - Gondy

4

使用java.util.function.Predicate接口:

Predicate<SomeObject> p1 = (SomeObject so ) -> so.getField()!=null;
Predicate<SomeObject> p2 = (SomeObject so ) -> so.getField().getSize() > 500;

...

 SomeObject someObject = new SomeObject();
 Predicate<SomeObject> fullPredicate = p1.and(p2).and( ...


 boolean result = fullPredicate.test(someObject);

除此之外,这将为您提供50个谓词一行定义,它们将更加紧凑。

3
我建议采用一种不同的方法:考虑使用验证器对象。也就是说,不要将所有检查都放在同一个方法中,而是将每个检查放在其自己的类中!
您需要定义一些验证器接口,提供验证方法。当验证失败时,该方法应该抛出一些ValidationException(该异常可能包含错误代码+消息)。
然后,您创建许多小类,每个类都实现该接口。
最后一步:创建一个列表,在其中放置每个实现类的一个对象。现在,您的代码归结为迭代该列表,并依次应用每个实现。
这样可以解耦您的验证步骤,并且添加新的/其他检查变得非常容易。

2
如果您特别希望使用lambda表达式,它们可以与枚举类型很好地配合使用:
public enum SomeValidators {
    E1 (1, o -> o.getField() == null),
    E2 (2, o -> o.getField().getSize() > 500)
    ;

    final int code;
    final Predicate<SomeObject> predicate;

    SomeValidators(int code, int predicate) {
        this.code = code;
        this.predicate = predicate;
    }
}

你可以按照以下方式使用它来复制你的if-else if流程:
boolean isValidObject(SomeObject o) {
    Optional<SomeValidators> firstError = 
        Arrays.stream(SomeValidators.values())
        .filter(v -> v.predicate.apply(o))
        .findFirst();

    firstError.ifPresent(e -> LOG.error("error code " + e.code));
    return firstError.isPresent();
}

1
我不确定在这里如何使用lambda表达式。
如果您正在使用大量的'if else'来处理许多业务规则,则可以尝试一些规则引擎。一个简单且最佳的选择是EasyRules
易于实现的EasyRules很方便,这将使您的业务逻辑代码看起来非常干净。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接