简体   繁体   English

使用while循环可避免Java中的if语句深度嵌套

[英]Use while loop to avoid deeply nested if statements in java

Hi I have written a little function like 嗨,我写了一些小功能

public void foo(MyClassA paraA) {
    if (paraA == null) return;
    MyClassB paraB = doSomeStuff(paraA);
    if (paraB == null) return;
    MyClassC paraC = doMoreStuff(paraB);
    if (paraC == null) return;
    ....
}

The above fails fast and is nice to read (ie the intention to return on null values is clear). 上面的方法很快失败,很容易阅读(即很清楚返回空值的意图)。 But now instead of simply returning, I want to do some error logging, so I changed to 但是现在,我不想简单地返回,而是要进行一些错误记录,所以我改为

public void foo(MyClassA paraA) {
    if (paraA == null) {doLog(); return;}
    MyClassB paraB = doSomeStuff(paraA);
    if (paraB == null) {doLog(); return;}
    MyClassC paraC = doMoreStuff(paraB);
    if (paraC == null) {doLog(); return;}
    ....
}

The above is also clean and easy to read, but I have to repeat doLog() a couple of times. 上面的代码也很干净而且易于阅读,但是我不得不重复doLog()几次。 So I change again to 所以我再次更改为

public void foo(MyClassA paraA) {
    if (paraA != null) {
        MyClassB paraB = doSomeStuff(paraA);
        if (paraB != null) {
            MyClassC paraC = doMoreStuff(paraB);
            if (paraC != null) {
                ....
                return;
            }
        }
    }
    doLog();
}

The above calls doLog() only just once but I ended with some deeply-nested if statements, which are very ugly and hard to read. 上面只调用了doLog()一次,但是我以一些深层嵌套的if语句结尾,这些语句非常难看且难以阅读。 So how do I keep the same cleanliness like before and have doLog() just once? 那么,如何保持与以前相同的清洁度并只做一次doLog()? Note that returning something else rather than void for foo() is not allowed. 请注意,不允许为foo()返回其他内容而不是void。 And I also read that using try/catch as oppose to null check is an anti-pattern. 而且我还读到,使用try / catch反对执行null检查是一种反模式。

If I am to try, I want to write something like 如果我想尝试,我想写一些类似的东西

public void foo(MyClassA paraA) {
    while(true) {
        if (paraA == null) break;
        MyClassB paraB = doSomeStuff(paraA);
        if (paraB == null) break;
        MyClassC paraC = doMoreStuff(paraB);
        if (paraC == null) break;
        ....
        return;
    }
    doLog();
}

The above fulfills all my needs(fail fast, clean, no nested if), but is the use of the while loop here an anti-pattern as the while loop here is never meant to run more than once? 上面的代码满足了我的所有需求(快速,干净,没有嵌套if),但是在这里使用while循环是否是一种反模式,因为while循环永远都不会运行超过一次?

Java has a nifty labeled break construct that might help you, here. Java在这里有一个漂亮的标记为break的构造,可能会对您有所帮助。

public void foo(MyClassA paraA) {
    block: {
        if (paraA == null) { break block; }
        MyClassB paraB = doSomeStuff(paraA);
        if (paraB == null) { break block; }
        MyClassC paraC = doMoreStuff(paraB);
        if (paraC == null) { break block; }
        ...
        return;
    }

    doLog();
}

If you used polymorphism better, you could do this: 如果您更好地使用了多态性,则可以执行以下操作:

public void foo(MyInterface para) {
    while (para != null) {
        para = para.doStuff();
    }
    doLog();
}

If you absolutely can't use polymorphism like that, use a dispatcher. 如果您绝对不能使用这种多态性,请使用调度程序。

But I've seen this before, and it looks like a state machine. 但我以前见过,它看起来像状态机。 Give "java enum state machine" a search. 给“ java枚举状态机”进行搜索。 I have a feeling that's what you're really trying to do. 我有一种感觉,这就是您真正想做的。

Do you think that this is clean 你认为这很干净吗

public void foo(MyClassA paraA) {

    MyClassB paraB = paraA != null?doSomeStuff(paraA):null;
    MyClassC paraC = paraB != null?doMoreStuff(paraB):null;

     if (paraC != null) {
         ....

     }

     doLog();
}

IMHO your second code snippet is what ypu should do. 恕我直言,您的第二个代码段是ypu应该执行的操作。

Do not try to make your code short. 不要试图使您的代码简短。 This is an antipattern. 这是一种反模式。

if (a==null) {
  log("Failed in step a");
  return;
}
B b = a.doSomething();

is very fast to read and understand . 阅读和理解非常快 You save nothing by compressing this code. 通过压缩此代码,您将一无所获。 Zero. 零。 Nada. 纳达。 Leave it to Hotspot VM, and focus on making code understandable. 将其留给Hotspot VM,并专注于使代码易于理解。 "if null then log return" is a classic, well understood and accepted pattern . “如果为null,则返回日志”是一种经典的,易于理解和接受的模式

It had become popular to try to make code "readable" with lambda antipatterns like this: 尝试使用如下lambda反模式使代码“可读”已成为流行:

B b = ifNullLog(a, () -> a.doSomething())

where 哪里

T ifNullLog(Object guard, Function<T> func) {
  if (guard == null) { doLog(); return null; }
  return func.run();
}

but IMHO this is a total antipattern. 但是恕我直言,这是一个完全反模式。 In fact, it is a best practise to even require braces for every if, else, for, while to make it easy to insert such a log statement without risking to break the code. 实际上,最好的做法是在所有情况下甚至都需要大括号,同时使插入这样的日志语句变得容易,而又不会冒破坏代码的风险。

Code like your first snippet: 像您的第一段代码一样:

if (a == null) return;

are dangerous. 是危险的。 Look at various bugs like Apples SSL disaster If someone adds doLog without noticing the missing brackets, the function will always return null. 查看各种错误,例如Apples SSL灾难如果有人在添加doLog时没有注意到括号,则该函数将始终返回null。 The apple SSL bug (or was it heartbleed?) esdentially was a 苹果的SSL错误(或令人流血的错误?)本质上是一个

if (a==null)
  return;
  return;
B b = a.doSomething();

See how subtle the bug is? 看看错误有多细微? You Java compiler will fortunately warn you if it's about unreachable code - it will not necessarily warn you otherwise... by always using brackets and well formatted code such bugs can easily be avoided. 您的Java编译器将幸运地警告您是否存在无法访问的代码-否则不一定会警告您...通过始终使用方括号和格式良好的代码,可以轻松避免此类错误。 Format code to avoid errors, not for aestetics . 格式化代码以避免错误,而不是出于美学目的

It is also well acceptable to use return codes . 使用返回码也是可以接受的。 Just don't make success default (see heartbleed again). 只是不要将成功设为默认值(请再次参见“流血”)。

Code c = execute(a);
if (c != Code.SUCCESS) {
  doLog(c);
  return;
}

where 哪里

Code execute(A a) {
  if (a == null) { return Code.FAILED_A_NULL; }
  B b = a.doSomething();
  if (b == null) { return Code.FAILED_B_NULL; }
  ...
  return Code.SUCCESS;
}

A classic use case of "return", another good pattern. 另一个很好的模式是“返回”的经典用例。

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

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