《代码整洁之道》读书笔记四


错误处理

抽离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就要求被调用函数需要一系列防御性检测,也就意味着程序有更大可能出错

TRANSLATE with x English
Arabic Hebrew Polish
Bulgarian Hindi Portuguese
Catalan Hmong Daw Romanian
Chinese Simplified Hungarian Russian
Chinese Traditional Indonesian Slovak
Czech Italian Slovenian
Danish Japanese Spanish
Dutch Klingon Swedish
English Korean Thai
Estonian Latvian Turkish
Finnish Lithuanian Ukrainian
French Malay Urdu
German Maltese Vietnamese
Greek Norwegian Welsh
Haitian Creole Persian  
Bing Webmaster Portal Back