《代码整洁之道》读书笔记(五)之错误处理
错误处理很容易将代码搞凌乱,这里介绍处理错误代码的一些技巧,努力编写一个既整洁又强固的代码。
1. 使用异常而非返回码
返回返回码的问题在于,它们搞乱了调用者代码。调用者必须在调用之后即可检查错误。不幸的是,这个步骤很容易被遗忘。所以,遇到错误时,最好抛出一个异常。
2. 先写Try-Catch-Finally语句
在编写可能抛出异常的代码时,最好先写出try-catch-finally语句。这能帮你定义代码的用户应该期待什么,无论try代码块中执行的代码出什么错都一样。
3. 使用不可控异常(unchecked exception)
可控异常有利有弊。使用可控异常的代价是违反开放/闭合原则。如果你在方法中抛出可控异常, 而catch语句在三个层级之上,你就得在catch语句和抛出异常处之间的每个方法签名中声明该异常。这意味着对软件中较低层级的修改,都将波及较高层级的签名。修改好的模块必须重新构建、发布,即便它们自身所关注的任何东西都没改动过。
4. 给出异常发生的环境说明
你抛出的每个异常,都应当提供足够的环境说明,以便判断错误的来源和处所。虽然可以从异常里追寻到Java stack trace,但是这个无法告诉你该失败操作的初衷。
因此,应当创建信息充分的错误消息,并和异常一起传递出去。在消息中,包括失败的操作和失败类型。
5. 依调用者需要定义异常类
当我们定义异常类时,最重要的考虑应该是它如何被捕获。
当底层抛出很多异常,但是我们并不需要这么细的划分时候,我们可以通过定义自己的异常,重新抛出我们的异常。另外,针对第三方API,我们可以对其的方法和异常进行一层封装,方便后续灵活替换。
6. 别返回null值
很多代码需要不断的检查返回值是否为null。这种代码糟糕透了。返回null值基本上是给自己增加工作量,也是在给调用者添乱。只要有一处没有检查null值,程序就会失控。
设想有这么一段代码:
List<Employee> employees = getEmployees();
if (employees != null) {
for (Employee e : employees) {
totalPay += e.getPay();
}
}
现在getEmployees()可能会返回null,但是否一定要这么做呢,如果修改getEmployees(),返回空列表,就能使代码整洁起来:
List<Employee> employees = getEmployees();
for (Employee e : employees) {
totalPay += e.getPay();
}
所幸Java有Collections.emptyList()
方法,该方法返回一个预定义的不可变列表,可用于这种目的:
public List<Employee> getEmployees() {
if ( .. there are no employees ..) {
return Collections.emptyList();
}
...
}
7. 别传递null值
在方法中返回null值是糟糕的做法,但将null值传递给其他方法就更糟糕了。除非API要求你向它传递null值,否则就要尽可能避免传递null值。