抛出异常以控制流-代码有异味吗?


问题内容

考虑以下代码(特别是Java):

public int doSomething()
{
    doA();

    try {
        doB();
    } catch (MyException e) {
        return ERROR;
    }

    doC();
    return SUCCESS;
}

其中doB()定义为:

private void doB() throws MyException

基本上,MyException仅在doB()满足某些条件(不是灾难性的,但确实需要以某种方式提高条件)的情况下存在,这样doSomething()才会知道会错误退出。

在这种情况下,您是否发现可以使用例外来控制流程?还是这是代码气味?如果是这样,您将如何重构呢?


问题答案:

这完全取决于错误条件是什么,以及方法的工作是什么。如果返回ERROR是为调用函数处理该错误的有效方法,那为什么会出错呢?

但是,通常这 一种气味。考虑一下:

bool isDouble(string someString) {
    try {
        double d = Convert.ParseInt32(someString);
    } catch(FormatException e) {
        return false;
    }
    return true;
}

这是一个很大的代码异味,因为您不希望出现双重值。您只想知道字符串是否包含双精度型。

有时,您使用的框架没有其他方式来完成您想要的事情。对于上述情况,有更好的方法:

bool isDouble(string someString) {
    bool success;
    Convert.TryParseInt32(someString, ref success);
    return success;
}

这类异常有一个特殊的名称,该名称是由我最近读过博客的人创造的,但可悲的是,我忘记了它的名字。如果您知道,请发表评论。最后但并非最不重要的一点是,上面是伪代码。我确定,我不是C#开发人员,因此上述内容无法编译,但是TryParseInt32/
ParseInt32我很好地证明了这一点,因此我将继续使用C#。

现在,到您的代码。让我们检查两个功能。一种闻起来,而另一种闻不到:

1.气味

public int setupSystem() {
    doA();

    try { doB(); }
    catch (MyException e)
    { return ERROR; }

    doC();
    return SUCCESS;
}

那是 代码的味道 ,因为当您要设置系统时,您不希望它失败。无法设置系统意味着您无法继续处理该错误。

2.好

public int pingWorkstation() {
    doA();

    try { doB(); }
    catch (MyException e)
    { return ERROR; }

    doC();
    return SUCCESS;
}

可以,因为该方法的目的是测试工作站是否仍然可以访问。如果不是,则这是该方法结果的一部分,而不是需要备用返回路径的特殊情况。