嵌入式if语句的替代方案?

时间:2011-12-09 15:22:55

标签: java if-statement

我有编程方面的历史,但在软件开发方面并不多。我正在为我工​​作的公司编写一个软件,我来挑战自己的代码的可读性。

我想知道这是否是嵌入式if语句的“有效”替代方案,或者我是否可以使用更好的方法。

假设我有以下方法:

public void someMethod()
{
    if (some condition)
    {
        if (some condition 2)
        {
            if (some condition 3)
            {
                // ...etc all the way until:
                doSomething();
            }
            else
            {
                System.err.println("Specific Condition 3 Error");
            }
        }
        else
        {
            System.err.println("Specific Condition 2 Error");
        }
    }
    else
    {
        System.err.println("Specific Condition 1 Error");
    }
}

现在我应该首先指出的是,在这种情况下,组合条件(与&&)是不可能的,因为每个都有一个我要报告的唯一错误,如果我将它们合并我不能那样做(或者我会吗?)。在有人尖叫“切换声明”之前,我应该指出的第二件事!在我看来,并非所有这些条件都可以通过switch语句来处理;一些是特定于对象的方法调用,一些是整数比较等。

那就是说,以下是使上述代码更具可读性的有效方法,还是有更好的方法呢?

public void someMethod()
{
    if (!some condition)
    {
        System.err.println("Specific Condition 1 Error");
        return;
    }

    if (!some condition 2)
    {
        System.err.println("Specific Condition 2 Error");
        return;
    }

    if (!some condition 3)
    {
        System.err.println("Specific Condition 3 Error");
        return;
    }

    doSomething();
}

基本上,我们不是检查条件并报告else块中的错误,而是检查条件的倒数并返回它是否为真。结果应该是相同的,但是有更好的方法来处理它吗?

8 个答案:

答案 0 :(得分:3)

是的,在这两种情况下,您的代码都有条件复杂性(code smells

Java是一种OOP语言,所以你的代码应该按照OOD的精神进行考虑,如下所示:

for (Condition cond : conditions) {
    if (cond.happens(params))
         cond.getHandler().handle(params);
}

条件列表应该注入此类,这样当添加或删除新条件时,类不会更改。 (open close principle

答案 1 :(得分:2)

我会把它写成

if (failing condition) {
    System.err.println("Specific Condition 1 Error");
} else {
    somethingExpensiveCondition2and3Dependon();
    if (failing condition 2)
        System.err.println("Specific Condition 2 Error");
    else if (failing condition 3)
        System.err.println("Specific Condition 3 Error");
    else
        doSomething();
}

答案 2 :(得分:2)

你的第二种方法相当不错。如果你想要更多巴洛克风格的东西,你可以将条件移动到Callable对象。每个对象也可以提供一种处理错误的方法。这使您可以在不牺牲功能的情况下编写任意长的一系列测试。

class Test {
    private final Callable<Boolean> test;
    private final Runnable errorHandler;

    public Test(Callable<Boolean> test, Runnable handler) {
        this.test = test;
        errorHandler = handler;
    }

    public boolean runTest() {
        if (test.call()) {
            return true;
        }
        errorHandler.run();
        return false;
    }
}

然后您可以按如下方式组织代码:

ArrayList<Test> tests;

public void someMethod() {
    for (Test test : tests) {
        if (!test.runTest()) {
            return;
        }
    }
    doSomething();
}

修改

以上是上述的更一般版本。它应该处理几乎所有这种类型的情况。

public class Condition {
    private final Callable<Boolean> test;
    private final Runnable passHandler;
    private final Runnable failHandler;

    public Condition(Callable<Boolean> test,
            Runnable passHandler, Runnable failHandler)
    {
        this.test = test;
        this.passHandler = passHandler;
        this.failHandler = failHandler;
    }

    public boolean check() {
        if (test.call()) {
            if (passHandler != null) {
                passHandler.run();
            }
            return true;
        }
        if (errorHandler != null) {
            errorHandler.run();
        }
        return false;
    }
}

public class ConditionalAction {
    private final ArrayList<Condition> conditions;
    private final Runnable action;

    public ConditionalAction(ArrayList<Condition> conditions,
            Runnable action)
    {
        this.conditions = conditions;
        this.action = action;
    }

    public boolean attemptAction() {
    for (Condition condition : conditions) {
        if (!condition.check()) {
            return false;
        }
    }
    action.run();
    return true;
    }
}

有人可能会想要添加某种可以传递以共享信息或收集结果的通用数据。我建议不要这样做,而是建议在实现条件和操作的对象中实现这样的数据共享,并保持这种结构不变。

答案 3 :(得分:2)

如果我特别迂腐,我会用这样的东西。

boolean c1, c2, c3;

public void someMethod() {
  boolean ok = true;
  String err = "";

  if (ok && !(ok &= c1)) {
    err = "Specific Condition 1 Error";
  }

  if (ok && !(ok &= c2)) {
    err = "Specific Condition 2 Error";
  }

  if (ok && !(ok &= c3)) {
    err = "Specific Condition 3 Error";
  }

  if ( ok ) {
    doSomething();
  } else {
    System.out.print(err);
  }
}

你现在单出退出并且持平。

<强>加

如果&amp; =对您来说很难,请使用以下内容:

  if (ok && !c3) {
    err = "Specific Condition 3 Error";
    ok = false;
  }

答案 4 :(得分:1)

对于这种情况,由于您同时拥有自定义条件和对每种条件的自定义响应,因此它就像您要获得它一样干净。

答案 5 :(得分:1)

您在实质上正在做的是在调用doSomething()方法之前验证某些条件。我会将验证提取到一个单独的方法中。

public void someMethod() {
  if (isValid()) {
    doSomething();
  }
}

private boolean isValid() {
  if (!condition1) {
    System.err.println("Specific Condition 1 Error");
    return false;
  }
  if (!condition2) {
    System.err.println("Specific Condition 2 Error");
    return false;
  }
  if (!condition3) {
    System.err.println("Specific Condition 3 Error");
    return false;
  }
  return true;
}

答案 6 :(得分:0)

不,那是关于你在Java中得到的东西。如果你有太多这些,它可能表明你应该重构一点,甚至可能重新考虑你的算法 - 尝试简化它可能是值得的,因为否则你将回到代码中几个月,想知道为什么a + b + c + d = ea + b' + c + d = zebra

答案 7 :(得分:0)

您拥有的第二个选项是更具可读性。虽然通常不建议多次返回,但是将所有这些返回放在代码的开头是明确的(并不是说它们分散在整个方法中)。另一方面,嵌套的ifs很难理解。