天天看点

代码整洁之道-第7章-错误处理-读书笔记

第 7 章 错误处理

  错误处理很重要,但如果它搞乱了代码逻辑,就是错误的做法。

7.1 使用异常而非返回码

  返回错误码,会搞乱调用者的代码。调用者必须在调用之后即可检查错误。遇到错误时,最好抛出一个异常。调用代码很整洁,其逻辑不会被错误处理搞乱。

7.2 先写 Try-Catch-Finally 语句

  异常的妙处之一是,它们在程序中定义了一个范围。执行 try-catch-finally 语句中 try 部分的代码时,你是在表明可随时取消执行,并在 catch 语句中接续。

  在某种意义上,try 代码块就像是事务。 catch 代码块将程序维持在一种持续状态,无论 try 代码块中发生了什么均如此。所以,在编写可能抛出异常的代码时,最好先写出 try-catch-finally 语句。这能帮你定义代码的用户应该期待什么,无论 try 代码块中执行的代码出什么错都一样。

  尝试编写强行抛出异常的测试,再往处理器中添加行为,使之满足测试要求。结果就是你要先构造 try 代码块的事务范围,而且也会帮助你维护好该范围的事务特征。

7.3 使用不可控异常

  在 java 中可以捕获的异常(Exception 类的子类)分为可控式异常和运行时异常量两种类型。

  可控式异常:在 java 中把那些可以预知的错误,例如从文件中读取数据,对数据库进行操作等,在程序编译时就能对程序中可能存在的错误进行处理,并给出具体的错误信息,我们把这些错误称为可控式异常。例如,IOException、SQLException、ClassNotFoundException、NoSuchFieldException 等等。

  运行时异常:在 java 中有些错误是不能被编译器检测到的,例如,在进行触发运算时,除数为零;试图把一个不是由数字组成的字符串使用 Integer 的 parseInt() 方法转换为整数等 java 的编译器是检测不到的,因而能正常编译,但是在运行时就会发生异常,我们把这些异常称为运行时异常。例如:IndexOutOfBoundsException、NullPointerException。

  可控异常的代价就是违反了开放/闭合原则。如果你在方法中抛出可控异常,而 catch 语句在三个层级之上,你就得在 catch 语句和抛出异常处之间的每个方法签名中声明该异常。这意味着对软件中较低层级的修改,都将波及较高层级的签名。修改好的模块必须重新构建、发布,即便它们自身所关注的任何东西都没改动过。

  如果你在编写一套关键代码库,则可控异常有时也会有用,你必须捕获异常。但对于一般的应用开发,其依赖成本要高于收益。

7.4 给出异常发生的环境说明

  你抛出的每个异常,都应当提供足够的环境说明,以便判断错误的来源和处所。在 java 中,你可以从任何异常里得到堆栈踪迹(stack trace);然而,堆栈踪迹却无法告诉你该失败操作的初衷。

  应创建信息充分的错误信息,并和异常一起传递出去。在消息中,包括失败的操作和失败类型。如果你的应用程序有日志系统,传递足够的信息给 catch 块,并记录下来。

7.5 依调用者需要定义异常类

  将第三方 API 打包是个良好的实践手段。当你打包一个第三方 API ,你就降低了对它的依赖:未来你可以不太痛苦地改用其他代码库。在你测试自己的代码时,打包也有助于模拟第三方调用。

  打包的好处还在于你不必绑死在某个特定厂商的 API 设计上。你可以定义自己感觉舒服的 API。而且能够写出更整洁的代码。

  对于代码的某个特定区域,单一异常类通常可行。伴随异常发送出来的信息能够区分不用错误。如果你想要捕获某个异常,并且放过其他异常,就使用不同的异常类。

7.6 定义常规流程

  特例模式(SPECIAL CASE PATTERN):创建一个类或配置一个对象,用来处理特例。客户代码就不用应付异常行为了。异常行为被封装到特例对象中。

7.7 别返回 null值

  返回 null 值容易引发错误。

  如果打算在方法中返回 null 值,不如抛出异常,或是返回特例对象。如果在调用某个第三方 API 中可能返回 null 值的方法,可以考虑用新方法打包这个方法,在新方法中抛出异常或返回特例对象。

  Java 有 Collections.emptyList() 方法,改方法返回一个预定义不可变列表,可用于生成空列表(特例对象)。这样编码,就能尽量避免 NullPointerException 的出现,代码也就更整洁了。

7.8 别传递 null 值

  在方法中返回 null 值是糟糕的做法,但将 null 值传递给其他方法就更糟糕了。除非 API 要求你向它传递 null 值,否则就要尽可能避免传递 null 值。

  在大多数编程语言中,没有良好的方法能对付由调用者意外传入的 null 值。事已如此,恰当的做法就是禁止传入 null 值。这样,你在编码的时候,就会时时记住参数列表中的 null 值意味着出问题了,从而大量避免这种无心之失。

7.9 小结

  整洁代码是可读的,但也要强固。可读与强固并不冲突。如果将错误处理隔离看待,独立于主要逻辑之外,就能写出强固而整洁的代码。做到这一步,我们就能单独处理它,也极大地提升了代码的可维护性。

7.10 文献