使用while循环可以避免在java中使用深层嵌套的if语句

时间:2015-02-13 07:00:06

标签: java loops if-statement while-loop nested-if

您好我写了一些像

这样的功能
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;
    ....
}

以上失败很快并且很好阅读(即明确返回空值的意图)。但现在我不想简单地返回,而是想做一些错误记录,所以我改为

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

以上也很简洁易读,但我必须多次重复doLog()。所以我再次改为

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

上面只调用了一次doLog(),但是我结束了一些深度嵌套的if语句,这些语句非常丑陋且难以阅读。那么如何像以前一样保持相同的清洁度并且只使用doLog()一次?请注意,不允许为foo()返回其他内容而不是void。我还读到使用try / catch作为反对null检查是一种反模式。

如果我要尝试,我想写一些类似

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

上面的内容满足了我的所有需求(快速失败,干净,没有嵌套if),但是在这里使用while循环是一个反模式,因为这里的while循环永远不会意味着运行多次?

3 个答案:

答案 0 :(得分:3)

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

如果你更好地使用了多态,你可以这样做:

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

如果绝对不能使用这样的多态,请使用调度程序。

但我以前见过这个,它看起来像一台状态机。给" java枚举状态机"搜索。我有一种感觉,就是你 想要做的事情。

答案 1 :(得分:1)

你觉得这很干净

public void foo(MyClassA paraA) {

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

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

     }

     doLog();
}

答案 2 :(得分:1)

恕我直言,你的第二个代码片段就是ypu应该做的事情。

不要试图让你的代码变短。这是一个反模式。

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

非常快速地阅读和理解。通过压缩此代码可以节省任何费用。零。纳达。将它留给Hotspot VM,专注于使代码易于理解。 "如果为null则记录返回"是一种经典的,易于理解和接受的模式

尝试制作代码"可读"已经变得很受欢迎。像lambda antipatterns这样:

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

,其中

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

但恕我直言这是一个完全反模式。实际上,最好的做法是为每个if,else,for提供 require 大括号,同时使插入这样的日志语句变得容易,而不会有破坏代码的风险。

代码就像您的第一个代码段一样:

if (a == null) return;

很危险。 查看Apples SSL灾难等各种错误 如果有人添加了doLog而没有注意到丢失的括号,则该函数将始终返回null。苹果SSL漏洞(或者是它的心脏病?)esdentially是

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

看看这个bug是多么微妙?你好Java编译器会警告你,如果它是关于无法访问的代码 - 它不一定会警告你...通过总是使用括号和格式良好的代码可以很容易地避免这样的错误。 格式化代码以避免错误,而非格式

使用返回代码也是可以接受的。只是不要默认成功(再次看到心声)。

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

,其中

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

&#34;返回&#34;的经典用例,另一种好的模式。