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

1. 使用异常而非返回码

在编码过程中,如果碰到错误的时候,建议抛一个异常

错误的例子

public class DeviceController{

	public void sendShutDown(){
		DeviceHandle handle=getHandle(DEV1);
		 //Check the state of the device 
		if (handle != DeviceHandle.INVALID){
			// Save the device status to the record field 
			retrieveDeviceRecord(handle);
			// If nat suspended,shut down
			if (record.getStatus()!=DEVICE_SUSPENDED){
				 pauseDevice(handle);
				 clearDeviceWorkQueue(handle);
				 closeDevice(handle);
			}else{
				logger.log("Device suspended. Unable to shut down"); 
			}
		}else{
			logger.log("Invalid handle for: " +DEV1.tostring()); 
	}
} 

正确的处理

public class DeviceController{

	public void sendShutDowm(){ 
		try{
			tryToShutDown();
		} catch (DeviceShutDownError e){ 
			logger.log(e);
		}

	private void tryToShutDown() throws DeviceShutDownError{
		 DeviceHandle handle =getHandle(DEV1);
		 retrieveDeviceRecord(handle);
		 pauseDevice(handle);
		 clearDeviceWorkQueue(handle);
		 closeDevice(handle);
	}

	private DeviceHandle getHandle(DeviceID id){
		throw new DeviceShutDownError("Invalid handle for:"+id.tostring()); 
	}
}

2. 先写try-catch-finally语句

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

3.使用不可控异常

不可控异常也叫做检查性异常,就是方法进行throw的异常

以某个大型系统的调用层级为例。顶端函数调用它们之下的函数,逐级向下。假设某个位于最底层级的函数被修改为抛出一个异常。如果该异常是可控的,则函数签名就要添加throw 子句。这意味着每个调用该函数的函数都要修改,捕获新异常,或在其签名中添加合适的throw子句。以此类推。最终得到的就是一个从软件最底端贯穿到最高端的修改链!封装被打破了,因为在抛出路径中的每个函数都要去了解下一层级的异常细节。既然异常旨在让你能在较远处处理错误,可控异常以这种方式破坏封装简直就是一种耻辱。

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

4. 给出异常发生的环境说明

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

5. 调用者需要定义异常类

不同的异常定义不同的类

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

6. 定义常规流程

先看一段业务逻辑

如果消耗了餐食,则计入总额中,如果没有消耗,则员工得到当日的餐食补贴。

实现1:

try{
	MealExpenses expenses = expenseReportDAO.getMeals(employee.getID());
	 m_total += expenses.getTotal();
)catch(MealExpensesNotFound e){ 
	m_total += getMealPerDiem(); 
}

实现2:

MealExpenses expenses=expenseReportDAO.getMeals(employee.getID());
m_total+=expenses.getTotal();

public class PerDiemMealExpenses implements MealExpenses{
	 public int getTotal(){
		// 默认每日津贴
		// return the per diem default 
	}
}

实现2的手法叫做特例模式(SPECIAL CASE PATTERN,[Fowler])。创建一个类或配置一个 对象,用来处理特例。你来处理特例,客户代码就不用应付异常行为了。异常行为被封装到特例对象中。

7. 别返回null值

方法的返回值尽量不要返回null值,好处是:避免NPE异常,调用方无需做非空判断,代码整洁

如果遇到了null,可以考虑:抛出异常、返回特例对象

8. 别传递null值

Integer -> int 拆箱

int -> Integer 装箱

方法的入参尽量不要传递null值,好处是:方法体无需进行非空判断,避免NPE异常,避免装箱、拆箱发生异常,代码整洁

9. 小结

即要保证代码可读性,也要保证代码的健壮性,异常逻辑切记要和正常逻辑进行隔离

本站文章资源均来源自网络,除非特别声明,否则均不代表站方观点,并仅供查阅,不作为任何参考依据!
如有侵权请及时跟我们联系,本站将及时删除!
如遇版权问题,请查看 本站版权声明
THE END
分享
二维码
海报
代码整洁之道-读书笔记之错误处理
在某种意义上,try代码块就像是事务。catch 代码块将程序维持在一种持续状态,无论 try代码块中发生了什么均如此。所以,在编写可能抛出异常的代码时,最好先...
<<上一篇
下一篇>>