zoukankan      html  css  js  c++  java
  • 什么时候使用异常

    先说个题外话: 在公司做了俩件事, 是我觉得很有意义的, 第一就是成立了一个PHP邮件组, 第二就是成立了一个Hi群. 目前俩者都有超过500 phpers在里面. 我一直认为, 构建一个交流平台, 让同学们能顺畅, 简单的沟通, 是营造积极的技术学习氛围的基础和前提. 让每个人的问题不会成为别人的问题, 则是最直接的利益. (后记: 不少人都问邮件组地址, 实在不好意思, 这个邮件组是公司内部的邮件组, Hi也是公司内部的. 谢谢)

    昨天, 有同事在邮件组提了个问题:

    PHP应该什么时候使用 Exception ? 它的性能如何?

    这个问题也算是一个久经争论的经典问题了. 我谈谈我的个人看法.

    异常与之对应的错误码(或者状态码), 到底各自有什么优点, 缺点, 我们应该怎么使用呢?

    错误码

    首先来说, 异常机制是在错误码机制之后才出现的, 那么根据进化论, 异常自然是避免了错误码机制的一些不足. 这些不足包括.

    1. 错误信息不丰富

    函数, 只能有一个返回值(当然, Lua可以返回多个, 但其实也相当于在PHP中返回一个数组), 我们见过最多的函数说明就是: 成功时候返回***, 错误的时候返回FALSE, 然而一个函数出错我原因可能有多种, 出错的种类更有多种. 一个简单的FALSE, 并不能把具体的错误信息告诉调用者.

    于是, 我们也就见过一些, 这样的函数说明: 如果返回值大于0, 则表示成功的状态码, 如果返回值小于0, 则表示出错的状态码.

    然而, 这个要求函数是返回整形(或者数字), 对于一些其他函数, 我们并不能通过0, >0, <0来判别, 并且, 即使通过这样的方式, 我们还需要用返回的错误码和一些预定义宏(或者调用类似strerror())来获取具体的, 可读的错误信息.

    于是, 就有一些函数使用全局的错误码, 和错误信息, 来保存具体的错误信息, 这个时候我们就看到这样的函数描述: 成功返回***, 出错的时候返回FALSE, 错误代码保存在全局变量$errno中(至少大多数Linux库函数是这样描述的, 呵呵).

    Okey, 这样的方式确实可以工作, 但是, 是不是觉得, 很丑陋呢?

    2. 加入错误状态码可能需要改变函数签名

    假设, 你编写了一个函数, 这个函数很简单, 很简单, 你认为他绝对不会出错, 于是你申明为(用C语言为例, PHP没有返回类型提示):

    1. void dummy() {
    2. }

    但是后来你慢慢修改了这个函数, 给了它更多的功能, 此时这个函数可能会失败了. 而你现在根本无法为这个函数, 加入错误返回码了.

    也许有人说PHP没有返回值类型限制一说, 但是想想PHP的构造函数, 构造函数是没有返回值的, 当发生错误的时候, 如果你不使用异常, 我想你只能选择die, 或者使用2中的方法来错误继续执行了.

    另外, 在一个良好的软件系统中, 返回类型其实也是约定俗成的, 当所有的使用的函数的地方, 都没有检查返回值的时候, 你还是无法为这个函数加入错误返回码.

    3. 错误状态码可能会被忽略

    当你的一个函数, 出错了, 返回了错误状态码, 而调用方并没有检测这个返回值, 会发生什么情况呢? -_#. 令一方面, 处处检测返回状态码, 会造成代码非常的,,ugly:

    1. <?php
    2.   if (!call1()) {
    3.       die();
    4.   }
    5.  
    6.   if (call2() != SUCCESS) {
    7.      die();
    8.   }
    9.  
    10.   if (call3() < 0) {
    11.       $msg = error_get_last();
    12.       die($msg["message"]);
    13.   }

    异常机制

    那么现在我们来看看异常机制, 如果我们采用异常机制, 上面的代码可以写作:

    1. <?php
    2. try {
    3.    call1();
    4.    call2();
    5.    call3();
    6. } catch (Exception $e) {
    7.    die($e->getMessage());
    8. }

    更方便的, 如果你的代码只是中间层, 你的调用方会负责处理错误的话, 你甚至可以简单的写作:

    1. <?php
    2. function myFunc() {
    3.    call1();
    4.    call2();
    5.    call3();
    6. }

    而一个异常对象, 可以包含更丰富的错误信息, 比如错误信息, 错误码, 错误的行数, 文件, 甚至出错上下文, 等等, 避免的”1.错误信息不丰富”的不足.

    我们也可以为一个返回void类型的函数增加异常, 而不改变他的函数签名, 也就不会有上面说的”2.加入错误状态码可能需要改变函数签名”. 对于PHP来说, 如果我们新加入的错误没有被捕捉, 也不用担心, 会明显的出错的. 也就不会发生上面所说的”3. 错误状态码可能会被忽略”的情况.

    然而, 也有一些反对使用异常的声音:

    1. 性能

    正如文章开头提问中的: “它的性能如何?”, 异常机制确实要比返回状态码的方式昂贵一些, 对于C++来说, 在异常发生的时候, 还要发生堆栈解退(对于PHP来说, 没有这个逻辑, 具体的大家可以参看我之间写的一篇文章: 深入理解PHP原理之异常机制).

    性能和方便, 往往是一个矛盾体, 我只能说, 你需要权衡, 如果你写的是一个小的模块, 并且它的生命期可能很短, 也不需要什么特殊的设计模式, 那我觉得你可以不用异常.

    而如果你在为一个庞大的软件做开发, 我想你更应该看重的, 应该是, 它的可扩展性, 可维护性.

    2. 太多可能的Uncaught Exception

    如果, 你调用了一个可能发生异常的函数, 但是却没有捕获这个异常, okey, Fatal Error了, 所以让我们的代码看起来:

    1. <?php
    2. try {
    3. } catch () {
    4. }
    5. ....
    6.  
    7. try {
    8. } catch () {
    9. }
    10. ....
    11. try {
    12. } catch () {
    13. }

    然而, 这个是可以经过良好设计避免的, 比如我在设计Yaf的时候, 就提供了全局异常处理, 也就是类似于, 你在最最顶层, 加上了一个try catch, 所有的异常错误逻辑都加到这个里面, 你也可以很方面的把你自己的异常加进去.

    结论

    经常有人批评我是俩面派, 呵呵, 但是在大家了解了上面的利弊以后, 是否也会和我一样认为: 这个事情没有定论呢? 一切从实际出发. :)

    说了这么多, 算是抛砖引玉, 欢迎补充

  • 相关阅读:
    netty系列之:基于流的数据传输
    netty系列之:中国加油
    netty系列之:Event、Handler和Pipeline
    netty系列之:netty中的Channel详解
    netty系列之:netty架构概述
    Python 列表解析式竟然支持异步?
    Python 数值中的下划线是怎么回事?
    Nginx+keepalived 双机主从模式下修改主Nginx自动同步nginx.conf配置文件至备Nginx
    Oracle nvarchar2存储特殊字符乱码问题
    Oracle 11.2.0.4.0版本下大表添加默认值字段影响因素
  • 原文地址:https://www.cnblogs.com/clphp/p/5253143.html
Copyright © 2011-2022 走看看