《代码整洁之道》读书笔记四
错误处理
抽离Try/Catch代码块
函数应该只做一件事,错误处理就是一件事。
// bad
public void delete(Page page) {
try{
deletePage(page);
registery.deleteReference(page.name);
configKeys.deleteKey(page.name.makeKey();
}catch(Expection e){
logError(e);
}
}
// good
public void delete(Page page) {
try{
// 将上例的操作,封装到一个方法
deletePageAndAllReferences(page);
}catch(Expection e){
logError(e);
}
}
使用非受控异常
受控异常:Checked Exception(FileException、SQLException等),这类异常必须写 try/catch,或者 throw抛出,否则编译通不过。
非受控异常:Unchecked Exception,这类异常也叫做运行时异常(与非受控异常 字数相等),这类异常不需要 try/catch,也不需要 throw抛出。即使 throw 了,上层调用函数也非必须捕获,编译能通过。
受控异常的代价就是违反开放/闭合原则。如果你在方法中抛出受控异常,这意味着每个调用该函数的函数都要修改,捕获新异常,或在其签名中添加合适的throw语句。对于一般的应用开发,受控异常依赖成本要高于收益成本,尽量 try/catch 处理,不要抛出。
给出异常发生的环境说明
应创建信息充分的错误信息,并和异常一起传递出去。在消息中,包括失败的操作和失败类型。如果你的应用程序有日志系统,传递足够的信息给catch块,并记录下来。
依调用者需要定义异常类
// bad
ACMEPort port = new ACMEPort(12);
try {
port.open();
} catch(DeviceResponseException e) {
reportPortError(e);
logger.log("Device response exception",e);
} catch(ATM1212UnlockedException e) {
reportPortError(e);
logger.log("Unlock exception",e);
} catch(GMXError e) {
reportPortError(e);
logger.log("Device response exception",e);
} finally {
// .....
}
通过打包调用API,确保它返回通过用异常类型,从而简化代码
// good
LocalPort port = new LocalPort(12);
try {
port.open();
} catch(PortDeviceFailure e) {
reportError(e);
logger.log(e.getMessage(),e);
} finally {
// .....
}
public class LocalPort{
private ACMEPort innerPort;
public LocalPort(int portNumber){
innerPort = new ACMEPort(portNumber);
}
public open() {
try {
innerPort.open();
} catch(DeviceResponseException e) {
// 自定义的异常类
throw new PortDeviceFailure(e);
} catch(ATM1212UnlockedException e) {
throw new PortDeviceFailure(e);
} catch(GMXError e) {
throw new PortDeviceFailure(e);
}
}
}
将第三方API打包是个良好的实践手段。当你打包一个第三方API,你就降低了对它的依赖。
其他
- try catch语句块的范围不要太大,这样不利于对异常的分析
- 别返回null值,这样可以减少调用者的防御性检测。与其返回null,不如抛出异常,或是返回特例对象(特例对象详见 p101)
- 别传递null值,传递null就要求被调用函数需要一系列防御性检测,也就意味着程序有更大可能出错

浙公网安备 33010602011771号