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

问题描述 投票:0回答:9

我有编程经验,但在软件开发方面没有太多经验。我目前正在为我工作的公司编写一个软件,我开始挑战自己代码的可读性。

我想知道这是否是嵌入 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 块中报告错误,而是检查条件的逆并在为真时返回。结果应该是一样的,但是有没有更好的方法来处理这个问题?

java if-statement
9个回答
3
投票

你的第二种方法相当不错。如果您想要一些更巴洛克风格的东西,您可以将您的条件移至 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;
    }
}

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


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

您现在是单出口且平坦。

已添加

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

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

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
投票

是的,你的代码在这两种情况下都带有条件复杂性的味道(代码味道

Java 是一种 OOP 语言,因此您的代码应该本着 OOD 的精神进行考虑,如下所示:

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

条件列表应该注入到此类中,这样当添加或删除新条件时,类不会更改。 (开闭原则)


1
投票

对于这种情况,这与您将得到的一样干净,因为您对每个条件都有自定义标准和自定义响应。


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

0
投票

不,这就是 Java 中的内容。如果您有太多这些,则可能表明您应该进行一些重构,甚至可能重新考虑您的算法 - 尝试稍微简化它可能是值得的,因为否则您将返回到中的代码几个月了,想知道为什么

a + b + c + d = e
但是
a + b' + c + d = zebra


0
投票

您拥有的第二个选项是更具可读性的。虽然通常不建议使用多个返回值,但将它们全部放在代码的开头是很清楚的(这并不是说它们分散在整个方法中)。另一方面,嵌套的 if 很难遵循和理解。


0
投票

雅加达验证

如果这些

if
测试对对象的字段强制执行规则,例如“年龄不能为负数”和“姓名长度必须至少有三个字符”,那么您可以考虑使用 Jakarta Validation。以前称为 Bean 验证

使用注释将规则附加到字段。

@Min( 0 )
int countGuests

雅加达验证提供了几条规则。您也可以编写自己的规则。

© www.soinside.com 2019 - 2024. All rights reserved.