• 2004-09-30

    返回值和异常

    Tag:向上走

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://www.blogbus.com/dreamhead-logs/417520.html

    返回值和异常有多大区别?
    下面是分别用返回值和异常两种方式实现的用户登陆密码校验的流程,
    返回值实现
    if (isValid(loginPwd, actualPwd)) {
        // login OK
    } else {
        // fail to login
    }

    异常实现
    try {
        validate(loginPwd, actualPwd);
        // login OK
    } catch (Exception e) {
        // fail to login
    }

    从上面的代码可以看出,二者从流程控制上来说,没有太多差别,很长一段时间内,我也确实认为二者没什么区别,直到我要完成一个文件移动的功能。

    JDK的File类中提供了一个renameTo的方法,用它完成文件移动简直再方便不过了,于是我很快的写出了一个方法:
    public static boolean moveFile(File file, String targetDir) {
        // Destination directory
        File dir = new File(targetDir);

        // Move file to new directory
        File dest = new File(dir, file.getName());
        return file.renameTo(dest);
    }

    代码很简单,将一个文件移动到有targetDir指定的一个目录中,经过简单的测试,确实可以完成文件的移形换位。我把它加入到了我的应用中,结果呢?没有成功。
    为什么?我也想知道为什么,可file.renameTo方法给我返回的是一个简单的boolean值,它只能告诉我操作是否成功。我不太关心如何成功,一旦false,我可想知道我错在哪里,毕竟,我愿意作个知错能改的好同志。现在,我只知道自己错了,错在哪里,无从知晓,这让我很是郁闷。只好听天由命,进行各种猜测。虽然历经种种艰辛,最终成功的消灭了这个bug,但我对renameTo已然深恶痛绝。

    这里就涉及到一个返回值还是异常的问题。
    正如前面所说,正常的情况下,我们没有什么顾虑,一旦发生错误,问题也就随之而来。仅仅通过一个简单boolean值不能表示出错误的具体原因,所以才会有为了一个错误我费尽周折。
    如果以异常的方式来处理呢?在编写良好的代码中,如果抛出异常,通常都携带一个错误信息,这样,只要我们把错误信息打印出来,真相便会大白于天下。

    改动一下设计,不用boolean值了,改用错误码呢?显然这么做没有问题,但实际上我们的处理仅仅包括两种可能:成功和失败。采用错误码的方式,会使可能性一下子膨胀许多,而且如果究竟有多少错误的可能往往是设计之初无法预料的,在未来有可能会不断的添加错误码,那相应的处理也会增加,等待我们的只是无边的烦恼。

    或许你认为应该用不同的异常表示不同的错误,不过,仔细想一下,这种做法似乎会有与使用错误码同样的麻烦,既然错误码已经麻烦不断了,显然这种做法也不是我们的首选。

    如果只用错误信息表示不同原因,我们岂不是要解析这个信息,这可不是很好的做法!
    不错,如果要在程序里得知错误原因,确实需要解析字符串,但程序里为什么要知道错误原因呢?
    不知道错误原因怎么改正呢?
    那为什么要在程序里做呢?通过打印出来的异常信息,我们不就知道如何处理了吗?如果真的需要不同的原因不同处理,那就用不同的异常吧!

    有时会有这种情况,既要让人知道错在哪里,又要保留这个错误原因,以便日后处理。比如在一个通信系统中,一个用户登陆失败后,流程上要拒绝这个用户,但系统需要记录这个错误,同时在结束这次会话之前,还要根据这个错误生成一个相应的应答给客户端返回。这种情况又该怎样处理呢?
    我们可以考虑将异常和错误码结合起来,一种手法就是在异常中夹带错误码。这样,用异常控制流程,用错误码记录错误原因,将二者的优点结合起来。
    一个不太恰当的例子是JDBC访问时可能会产生的SQLException。它把错误原因以错误码的形式保存在异常之中,通过getErrorCode的方法得到这个错误码。说它不太恰当的原因是,SQLException实际上还有可以细分的余地,比如数据库连接出现的问题不应该与SQL语言的错误混杂在一起,毕竟SQL语言的错误一般都会在调试阶段得到解决,而数据库连接的错误运行时依然如故。

    好了,总结一下吧!
    如果用以控制流程,在返回值和异常中,可以优先考虑使用异常。
    在异常处理中,不同的错误原因,如果是给人看,可以用错误信息的形式夹带在异常中,如果用以控制流程,就用不同的异常表现出来。
    如果确实需要,可以将二者结合起来。

    分享到:

    历史上的今天:

    引用地址:

    评论

  • 这个问题我也很有体会;我觉得声明中抛出一个父异常,实际抛出子异常是个不错的做法,就像SQLException示范的
    回复yally说:
    你的评论给了我新的思路,子类直接夹带错误码,抛出子类可以获得错误,捕获父类可以控制流程。
    2004-10-14 19:57:26