有哪些更好的方法来避免Java中的do-while(false);黑客行为?

9
当代码流程如下时:
if(check())
{
  ...
  ...
  if(check())
  {
    ...
    ...
    if(check())
    {
      ...
      ...
    }
  }
}

我通常看到的解决方法是避免这种混乱的代码流:

do {
    if(!check()) break;
    ...
    ...
    if(!check()) break;
    ...
    ...
    if(!check()) break;
    ...
    ...
} while(false);

有没有一些更好的方法来避免这种变通/黑客,使其成为更高级别(行业级别)的代码?
也许有些结构来自Apache commons或Google Guava?
注意:这是相同问题的C++版本的副本。那里最好的答案是真正的函数指针和GOTO命令。这两个在Java中都不存在。我非常感兴趣了解Java的同样情况。
将其放入一个新函数并使用return,在我看来不是一个好的解决方案,因为返回会退出该方法。因此,如果我的类有20个带有这些结构的方法,我需要添加20个额外的方法才能完成这项工作。这就是为什么GOTO是C ++的最佳答案的原因。

6
有些答案仍然适用——将其移至自己的函数中,并使用return作为例子。 - Sinkingpoint
@MarounMaroun 因为你可以在一个函数中使用它20次来传递20个串行步骤。如果我这样做,最终的if语句就会向右滑动20个选项卡,这使得代码难以阅读。 - Kenyakorn Ketsombut
3
我不是一个虔诚的信徒,但我确信“GOTO”不是与Java相关的任何问题的正确答案。 - WarrenFaith
@Quirliom,我在问题中添加了一句话,为什么不好,可惜你回答得太快了 :) 还有哪些其他答案是正确的?我没有看到任何一个。 - Kenyakorn Ketsombut
dowhile(false);部分是不必要的 - 只需使用一个普通块({ if (!check()) break; })即可。 - Sam Marsh
@KenyakornKetsombut, Java 曾经有过 GOTO 的支持,但是由于出现了更好的解决方案(我认为其中包括 C++),它被移除了:https://dev59.com/LHE85IYBdhLWcg3w64IA#4547764。除此之外,在链接的帖子中,GOTO 答案并不是“C++ 中最好的答案”(至少如果你考虑 upvotes 的话)。 - WarrenFaith
4个回答

6

1
我接受这个答案,因为它与原始来源非常接近。当然,其他答案也很好。这个答案恰好解决了C++(和Java)用户所遇到的问题,并且在Java中显然有一个非常简单的解决方案。感谢您的贡献。 - Kenyakorn Ketsombut

6

为了降低圈复杂度,你可以将逻辑分离成子方法:

public void go() {
    first();
}

public void first() {
    if(check()) {
        // #1
        second();
    }
}

public void second() {
    if(check()) {
        // #2
        third();
    }
}

public void third() {
    if(check()) {
        // #3
    }
}

感谢您的回复。是的,您对于复杂性的看法是正确的。然而,在这个问题中,重点更多地放在可读性和代码美观上。速度并不是主要目标。我认为现代JIT编译器会很好地处理它。 - Kenyakorn Ketsombut

2
因为你可以在一个函数中使用这个20次来传递20个连续的步骤。如果我像这样做,最后的if幻灯片已经向右移动了20个选项卡,这使得代码难以阅读。
你的评论显示了问题所在: 你想象了错误的代码结构。
例如,你的代码是这样的:
if (bool) {
     // do something
     if (anotherBoolean) {
         //do even more
         if (thirdBoolean) {
             // here we go! I spare deeper structure...
         }
     }
}

这可以很容易地进行重构:
public void method() {
    if (bool) {
        doSomeStuff();
    }
}

public void doSomeStuff() {
    if (anotherBoolean) {
        doThirdStuff();
    }
}

public void doThirdStuff() {
    if (thirdBoolean) {
        doEvenMore();
    }
}

public void doEvenMore() {
    // deeper structure
}

那种代码结构是 Clean Code 的一个很好的样例,因为方法都在做它们的“单一目的”事情,而不是使用 while 循环进行 hack,并且你甚至可以在其他地方重用这些方法。

感谢您的贡献。在我的情况中,我的类中有4种方法,它们运行了6个布尔值。因此,我必须向该类添加24种方法。现在尝试为这些方法找到24个不同的名称。好的,我同意,这取决于我的实际代码。除了寻找更多名称外,我可以添加参数等。因此,您的解决方案可能适用于特定情况。我认为有人可能会想到比创建新方法更酷的东西,例如switch、exception-catches或static-imports。除了创建新方法之外的任何其他方法。 - Kenyakorn Ketsombut
1
如果你有六个布尔值代表某种状态,可以尝试使用枚举并在switch case语句中使用它们。例如,我用它来处理输入。例如State.PRESSED、RELEASE、MOVING、NONE等,并根据该状态调用相应的方法。 - WarrenFaith

1

这是一个好问题。任何解决方案的语义必须涉及某种可退出的兴奋块,可以是文字或由实现隐含。

其他答案有些过于复杂,虽然避免了do-while循环,但失去了所有清晰度和可读性,使它们成为“糟糕”的代码。

经过一些思考,以下是我编写的代码:

doSomething(); // Replace current code with a method call

private void doSomething() {
    if(!check()) return;
    ...
    ...
    if(!check()) return;
    ...
    ...
    if(!check()) return;
    ...
    ...
}

主要内容:

  • 一个方法是一个代码块,可以通过return干净地退出
  • 这三个部分明显作为一个编程单元工作,因此它们一起是重构成一个独立方法的良好候选
  • 没有“人为构造”的东西,比如毫无意义的do-while(false)结构,只存在于提供可退出块以及可能会让其他人困惑
  • 通过使用负面测试然后退出,可以实现两个目标:
    • “尽早退出”编码范例
    • 减少缩进/嵌套,从而降低循环复杂度
  • 不会创建额外的块/类/对象,保持可读性和性能(尽管对于方法调用来说,一个框架将被推入堆栈——影响微乎其微)

咦?踩票?能分享一下为什么吗?我觉得这种方法还不错(甚至很好)。 - Bohemian

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