用保护子句替换嵌套条件的模式,同时仍然对绝大多数情况(但不是全部)执行某些操作

Pattern to replace nested conditionals with guard clauses while still performing some action for the vast majority of cases but not all

我有一种方法遵循以下模式:

void myMethod(String arg1) {
  SomeObject foo = getSomeObject(arg1);

  if(foo != null) {
    SomeOtherObject bar = foo.getSomeOtherObject();

    if(bar != null) {
      bar.doSomething();

      if(bar.isGood()) {
        YetAnother baz = getAnotherByName(bar.getAnotherName());

        if(baz != null) {
          if(baz.exitEarly()) {
            foo.recordEarlyExit(baz.getName());
            return;
          }
        }
      }
    }
  }
  doNormalThing();
}

我想知道是否有更简洁的方法可以在没有这么多级别的情况下获得相同的行为。

如果我不必在最后做 doNormalThing();,我可以做这样的事情:

void myMethod(String arg1) {
  SomeObject foo = getSomeObject(arg1);
  if(foo == null) { return; }

  SomeOtherObject bar = foo.getSomeOtherObject();
  if(bar == null) { return; }

  bar.doSomething();
  if(!bar.isGood()) { return; }

  YetAnother baz = getAnotherByName(bar.getAnotherName());

  if(baz == null) { return; }

  if(baz.exitEarly()) {
    foo.recordEarlyExit(baz.getName());
  }
}

我基本上是在执行上述操作,但在所有 return 之前添加 doNormalThing();。但这是很多重复,如果我需要更改某些内容,我必须在所有这些块中进行。

我可以做一些事情,比如将它包装在 try{ ... } catch (DoNormalThingException e) { doNormalThing(); } 中,并且只在我想在从方法返回之前调用该方法时抛出 DoNormalThingException,而在 [=17= 时不抛出它] 是真的。但这似乎也不干净,而且似乎在滥用异常。

如果它只是检查所有条件中的 null,那么我可以包装在 try 中,并且只有 运行 doNormalThing()NullPointerException 被抛出。它比其他基于异常的方法干净得多:我不需要任何条件,而且我正在捕获一个合法的异常,而不是一个仅用于控制流的虚构异常。但并非所有检查都是针对 == null 的,我不想掩盖来自更深层方法调用的 NPE。

如果 java 有 goto...

编辑:所以我知道这种类型的重构有一个名字,它是:用保护子句替换嵌套条件。这就是我试图做的,但如果你有在大多数情况下但不是全部情况下需要调用的额外方法,它就不会很好地工作。不过,我想我会提到它以帮助其他人找到这个问题。

一个选项是将 doNormalThing 方法调用以外的所有方法包装在另一个方法中,returns 之后是否执行 doNormalThing:

void myMethod(String arg1) {
    if (myMethodInternal(arg1)) {
        doNormalThing();
    }
}

private boolean myMethodInternal(String arg1) {
    SomeObject foo = getSomeObject(arg1);
    if (foo == null) {
        return true;
    }

    SomeOtherObject bar = goo.getSomeOtherObject();
    if (bar == null) {
        return true;
    }
    // etc

    if (baz.exitEarly()) {
        foo.recordEarlyExit(baz.getName());
        return false;
    }
    return true;
}

感觉不是很干净,但至少可以用。您 可以 也毫无例外地使用 try/finally - 使用 boolean 字段来确定是否执行 doNormalThing.

你可以通过组合 Java 8 个可选值来做到这一点:

void myMethod(String arg1) {
    SomeObject foo = getSomeObject(arg1);

    Optional<SomeOtherObject> opBar = 
                Optional.ofNullable(foo)
                        .map(foo -> foo.getSomeOtherObject())

    opBar.ifPresent(bar -> bar.doSomething());

    Optional<YetAnother> opBaz = 
                  opBar.filter(bar -> bar.isGood())
                       .map(bar -> getAnotherByName(bar.getAnotherName())
                       .filter(baz -> baz.exitEarly());

    opBaz.ifPresent(baz -> foo.recordEarlyExit(baz.getName()));

    if (!opBaz.isPresent()) {
        doNormalThing();
    }
}