【问题标题】:Java 8 avoiding lots of if/else statementsJava 8 避免大量 if/else 语句
【发布时间】:2017-03-20 18:05:56
【问题描述】:

我有一些看起来像这样的东西:

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

用 lambdas 在 java 8 中写这个最简洁的方法是什么?

【问题讨论】:

  • 为什么要使用 lambdas?
  • 您可以使用 lambda,但我怀疑这将是更难理解的代码。
  • 我同意 Reimeus 和 Peter 的观点。它的可读性会降低
  • 我不想使用 lambda,我想摆脱 50 个 if/else 语句。
  • 提示:Bean Validation

标签: java validation lambda predicate


【解决方案1】:

为此使用多态性。为每个逻辑验证器创建一个类并将它们链接到列表中。这是您需要的很好的答案: https://stackoverflow.com/a/23501390/1119473

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
}

【讨论】:

  • 这实际上是正确的解决方案(或使用做同样事情的库)。我不知道为什么它被否决了。它可以通过添加Validator 的示例实现来改进,该示例实现特定于 OP 的问题以及如何调用它。无论如何,我要 +1
  • 我没有投反对票,但是如果您按每行代码获得报酬,那么将每个条件变成一个新类是很好的,但为什么要将一种方法变成 50 个类。这真的是个好主意吗?
  • 没有人为代码行付费。通过使用模式,您正在创建可读的代码。作者说:“我想去掉 50 个 if/else 语句。” -- 这很好地完成了工作。
  • 有趣,两个人,同样的想法。如果我有一个真正的键盘,我的答案会首先出现 :-) 和 plus1 用于我遗漏的代码示例。
  • 顺便说一句:由于Validator 是一个单抽象方法类,验证器可以写为lambda,从而减少了代码行数。例如:Validator v = obj -&gt; obj.getField().getSize() &gt; 500 ? Result.OK : Result.FAILED;
【解决方案2】:

我可能会返回错误,但这仍然会使用一些 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;
}

但是,就个人而言,这并不更清楚,并且更难调试,例如断点。

【讨论】:

  • 你已经用 lambdas 很好地解决了这个问题,但是这段代码的逻辑点是什么?我在这里看到了一些验证。如果您的验证变得更复杂怎么办?调试愉快..我不会在现实世界的项目中使用这段代码
【解决方案3】:

使用 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 个谓词单行定义之外,它们会更紧凑一些。

【讨论】:

    【解决方案4】:

    我推荐使用不同方法的解决方案:考虑使用 Validator 对象。含义:不是将所有检查都放在同一个方法中,而是将每个检查放在自己的类中!

    您定义了一些提供验证方法的验证器接口。当验证失败时,该方法应该抛出一些 ValidationException (并且该异常可能包含错误代码+消息)。

    然后您创建许多小类,每个小类都实现该接口。

    最后一步:创建一个列表,在其中放置每个 impl 类的一个对象。现在您的代码归结为迭代该列表,并一个接一个地应用每个 impl。

    这将您的验证步骤解耦,添加新的/其他检查变得超级容易。

    【讨论】:

      【解决方案5】:

      如果您特别希望使用 lambda,它们可以与 enum 很好地结合:

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

      【讨论】:

        【解决方案6】:

        我不确定 lambdas 在这里有什么用处。

        如果您使用大量“if else”来处理大量业务规则,那么一种选择是尝试一些rule engines。一个简单且最佳的选择是EasyRules

        Easy rules 方便且易于实现。这将使您的业务逻辑代码看起来非常干净。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-11-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多