clean-code: 错误处理

下面就Clean code 中关于如何优雅的处理一些错误代码总结的一些技巧和思路。

1、使用异常而非返回码

请看下面的代码清单, DeviceController.java

public class DeviceController {
...
public void sendShutDown(){
//Check the state of the device
if(handle != DeviceHandle.INVALID){
//Save the device status to the record field
DeviceRecord record = retrieveDeviceRecord(handle);
//If not suspended, shut down
if(record.getStatus() != DEVICE_SUSPENDED){
pauseDevice(handle);
clearDeviceWorkQueue(handle);
closeDevice(handle);
}else {
logger.log("Device suspende.");
}
} else{
logger.log("invalid handle for:" + DEV1.toString());
}
}
...
}

我想大家在项目中都碰到过类似的嵌套检查。这类手段的问题在于,他们搞乱了调用者代码。调用者必须在调用之后即可检查错误。不幸的是,这个步骤很容易被遗忘。所以,遇到错误时,最好抛一个异常。调用代码很整洁,其逻辑不会被错误处理搞乱。
下面的代码清单展示了方法中遇到错误时抛出异常的情形。
代码清单: DeviceController.java(采用异常处理)

public class DeviceController {
...
public void sendShutDown(){
try{
tryToShutDown();
}catch(DeviceShutDownError e){
logger.log(e);
}
}

private void tryToShutDown() throws DeviceShutDownError{
DeviceHandle handle = getHandle(DEV1);
DeviceRecord record = retrieveDeviceRecord(handle);

pauseDevice(handle);
clearDeviceWorkQueue(handle);
closeDevice(handle);

}

private DeviceHandle getHanle(DeviceID id){
...
throw new DeviceShutDownError("invalid handle for: " + id.toString());
...
}

private DeviceRecord retrieveDeviceRecord(DeviceHandle handle){
...
throw new DeviceShutDownError("Device suspended.");
...
}
...
}

这段代码就整洁了很多,当然多出了一些异常类,但这是不可缺少的。。。
现在,对两个元素设备的关闭算法和错误处理已经被隔离了,我们可以查看其中任一元素,并分别理解和处理他们,而不必去修改业务逻辑代码。

2、根据调用者需要来定义异常类。
当我们在应用程序中定义异常时,最重要的考虑应该是“他们如何被捕获”。
来看一个很常见的代码。
代码清单:


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,确保它返回通用异常类型,从而简化代码。
代码清单:

LocalPort port = new LocalPort(12);
try{
port.open();
}catch (PortDeviceFailure e){
reportError(e);
logger.log(e.getMessage(),e);
}finally{
...
}

LocalPort类就是个简单的打包类,捕获并翻译由ACMEPort类抛出的异常:
public class LocalPort{
private ACMEPort innerPort;

public LocalPort(int portNumber){
innerPort = new ACMEPort(portNumber);
}
}

public void 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);
}
}
...
}

类似我们为ACMEPort定义的这种打包类非常有用,这也可以看作是对第三方API做了一个代理,实际上,将第三方API打包是个良好的实践手段。当打包一个第三方API,就降低了对它的依赖:未来你可以不太痛苦地改用其他代码库。而且在测试自己的代码时,打包也有助于模拟第三方调用。
3、别返回null值
对于NullPointerException,想必大家都熟悉得不能再熟悉了。那么如何尽量避免空指针异常呢?其一就是尽量避免返回null值,可以用一个特例对象来代替。如果是调用第三方API返回null值,可以在新方法中抛出异常或返回特例对象,比如在Java中如果返回的是一个list,如果返回为null,完全可以用Cloolections.emptyList()来代替,这样调用者就不必来检查获取的结果是否为null了。
4、别传递null值,在大多数编程语言中,没有良好的方法能对付由调用者意外传入的null值,事已至此,恰当的做饭就是禁止传入null值。

以上代码是阅读Clean code第七章后做的一些小笔记,大家可以共同学习。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值