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


    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()); 
    	}
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    正确的处理

    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()); 
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    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(); 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    实现2:

    MealExpenses expenses=expenseReportDAO.getMeals(employee.getID());
    m_total+=expenses.getTotal();
    
    public class PerDiemMealExpenses implements MealExpenses{
    	 public int getTotal(){
    		// 默认每日津贴
    		// return the per diem default 
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

    7. 别返回null值

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

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

    8. 别传递null值

    Integer -> int 拆箱

    int -> Integer 装箱

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

    9. 小结

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

  • 相关阅读:
    Python 使用类的属性和方法
    微擎模块 抖抖赢口红1.1.5小程序 前端+后端 优化分销升级功能
    【cesium】3D Tileset 模型加载并与模型树关联
    2022微信小程序:解决消除button的默认样式后容器任然在中间的位置的方案
    信息系统项目管理教程(第4版):第二章 信息技术及其发展
    RabbitMQ初步到精通-第四章-RabbitMQ工作模式-SIMPLE
    为什么调用父类构造函数,super 必须是构造函数内的第一条语句?
    容联云首发基于统信UOS的Rphone,打造国产化联络中心新生态
    22.3D等距社交媒体菜单的悬停特效
    Jx_Python基础库入门_1: Python简介
  • 原文地址:https://blog.csdn.net/constant_rain/article/details/127646589