如何优雅地进行错误处理(clean code阅读笔记之六)
错误处理是十分必要的,但是如果对错误处理使用不当则会让代码变得十分臃肿,让阅读者看不清代码的逻辑,更严重的是,这也会让程序变得十分脆弱。本文中将列出一些使用错误处理的技巧,帮助你写出既简洁又健壮的代码。
使用Exception而不是返回码
返回码是一个历史遗留问题,在以前的没有Exception的语言(比如c语言)中,它是有效且必要的,但是在有Exception的语言中使用返回码是没有任何益处的。
对于使用返回码的函数,调用者在得到调用结果(这里是返回码)之后要立即去验证返回码,这对于代码的可读性和结构的合理性都是极大的挑战,使用「异常处理」能让业务逻辑和错误处理在代码结构上分离,代码的结构和逻辑会更清晰。
首先把try-catch-finally块写出来
当遇到需要做异常处理的时候,首先把try-catch-finally块写出来,这能帮助你写出更好的异常处理代码。
永远使用unchecked异常
这又是一个非常有争议性的话题,到底是使用checked异常,还是unchecked异常。
_checked异常(通常是Exception的子类)是Java中的一个概念,它是指如果一个方法抛出此异常,那么它就必须被显式地捕获或者传递,通常是在方法上添加声明。
unchecked异常指的是可能随时抛出的异常(通常是RuntimeException,Error,和他们的子类)。
推荐阅读:Unchecked Exceptions — The Controversy,在这篇Oracle的官方指南中讲到:如果你要从某个异常中恢复,那么就使用checked异常,否则使用unchecked异常,意思就是unchecked的异常表示系统出问题了,那么必须停下来,去检查到底什么问题,然后解决。_
作者的观点是:如果在某些特殊的情况下必须要捕获异常并作出处理,那么不得不使用checked异常。但是在通常的开发过程中应当避免使用checked异常。
checked异常的使用确实带来了一些好处,但是也同时也造成了一些问题,最重要的是它违反了「开闭」原则。比如如果一个方法抛出某个checked异常,而这个异常的处理(通常是catch)是在3个方法调用层级之上,那么当你修改最底层这个方法的抛出异常时,所有在这个调用链中的方法签名都需要被修改。这明显违反了「对修改关闭」的原则,也违反了面向对象中「封装内部实现」的特性。
提供异常的上下文
在异常中一定要提供可供这个异常的捕获者(通常是在catch中)用来分辨这个异常的上下文信息,使之可以通过log或其他方式最终呈现出来。
按照调用者的需求定义异常类
当我们定义一个异常类时,可以按照这个异常类所属的模块,抑或是这个异常类的类型来定义,但是当我们在程序中定义一个异常类时,更多的则应该是在考虑「这个异常会在什么情况下被捕获」。代码6-1中展示了普通的按照类型定义的异常类,
//代码6-1
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");
} finally {...
}
代码6-2中展示了使用一个包装类定义的一个新的异常类,不仅代码看起来更加清晰简单,而且还会带来更多其他的好处。
//代码6-2
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 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);
}
}
...
}
像代码6-2中那样对于第三方的类库中的API进行封装会带来很多好处。实际上,封装第三方API可以算是一种最佳实践。
当你在你的项目中对第三方类库进行了自己的封装之后,相当于解耦了你的项目和这个第三方类库,你可以轻易的将这个类库更换掉,更重要的是,你可以不需要一定遵循这个类库的设计来使用它,比如代码6-2中所示的,本来类库中定义了多个重合
定义常规流程
虽然我们可以写出很好的错误处理代码,它们外形优雅、结构清晰,但是有时候我们并不是想要「终止代码的逻辑流程」而抛出异常,这种情况就不要使用异常类来处理,应该使用更常规的流程来处理,比如代码6-3中异常处理。
//代码6-3
try {
MealExpenses expenses = expenseReportDAO.getMeals(employee.getID());
m_total += expenses.getTotal();
} catch(MealExpensesNotFound e) {
m_total += getMealPerDiem();
}
代码6-3中的错误处理只是为了处理一种特殊状况(可能是数据库中找不到此次吃饭的消费情况),而不是要中止计算而抛出异常,那么它可以被重构为代码6-4中的样子。
//代码6-4
MealExpenses expenses = expenseReportDAO.getMeals(employee.getID());
m_total += expenses.getTotal();
//这里引入一个新的特殊情况的类
public class PerDiemMealExpenses implements MealExpenses {
public int getTotal() {
// return the per diem default
}
}
这种编程模式被叫做特殊情况模式,它使用一个新的类来处理这种特殊情况,那么就不需要抛出异常类,因为在异常的情况下也会返回一个MealExpenses对象,那么整个流程看起来就和普通的业务流程一样了。
不要返回Null
Null是邪恶了,不要在代码中返回Null。
如果你的代码中有返回Null的情况,那么在代码的任何地方都可能抛出NullPointerException而导致系统崩溃退出,你的代码中也可能会存在一大堆的判断「一个对象是否为Null」的代码,这可能是所有开发者都不愿意看到的情况,可以使用代码6-4中所使用的特殊情况模式来避免返回Null,永远返回一个有值的对象。
不要传递Null
在函数调用的参数中传递Null是比返回Null更邪恶的事情。所以在你自己的代码中,永远不要传递Null。
作者:TheAlchemist
链接:https://www.jianshu.com/p/b3760c7a0815
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
如何优雅地进行错误处理(clean code阅读笔记之六)的更多相关文章
- “Clean Code” 读书笔记序
最近开始研读 Robert C.Martin 的 “Clean Code”,为了巩固学习,会把每一章的笔记整理到博客中.而这篇博文作为一个索引和总结,会陆续加入各章的笔记链接,以及全部读完后的心得体会 ...
- Clean Code读书笔记
第一章 整洁代码 1.编程要做什么 代码呈现了需求的细节,在某些层面上,这些细节无法被忽略或抽象,必须明确.而将需求明确到机器可以执行的细节程度,就是编程要做的事. 2.项目过程中经常遇到这样的问题: ...
- 《代码整洁之道》(Clean Code)- 读书笔记
一.关于Bob大叔的Clean Code <代码整洁之道>主要讲述了一系列行之有效的整洁代码操作实践.软件质量,不但依赖于架构及项目管理,而且与代码质量紧密相关.这一点,无论是敏捷开发流派 ...
- 《clean code》讲述代码中的道,而不是术
Clean code 看<clean code>一书,学习高手写出的代码,简单高效的代 1.目标 Bjarne Stroustrup:优雅且高效:直截了当:减少依赖:只做好一件事 Grad ...
- 代码整洁之道Clean Code笔记
@ 目录 第 1 章 Clean Code 整洁代码(3星) ?为什么要整洁的代码 ?什么叫做整洁代码 第 2 章 Meaningful Names 有意义的命名(3星) 第 3 章 Function ...
- Writing Clean Code 读后感
最近花了一些时间看了这本书,书名是 <Writing Clean Code ── Microsoft Techniques for Developing Bug-free C Programs& ...
- 《Clean Code》 代码简洁之道
作者介绍 原文作者: Robert C. Martin, Object Mentor公司总裁,面向对象设计.模式.UML.敏捷方法学和极限编程领域的资深顾问,是<敏捷软件开发:原则.模式.与实践 ...
- 聊聊clean code
clean code,顾名思义就是整洁的代码,或者说清晰.漂亮的代码,相信大多数工程师都希望自己能写出这样的代码. 也许这是个千人千面的话题,每个工程师都有自己的理解.比如我,从一个天天被骂代码写得烂 ...
- Clean Code之JavaScript代码示例
译者按: 简洁的代码可以避免写出过多的BUG. 原文: JavaScript Clean Code - Best Practices 译者: Fundebug 本文采用意译,版权归原作者所有 引文 作 ...
随机推荐
- OO 面向对象的三大特性
面向对象的三大特性 一.面向对象特性——封装: 概念:把对象所能操作的信息进行封装: 封装作用: 1.减少代码之间的耦合: 2.提供统一的访问接口,内部修改不影响外部的调用:(开放封闭原则) 二.面向 ...
- 巧用Scrum与Kanban
本文来自网易云社区 文\屈鹏飞 在互联网行业的项目管理实践中,敏捷和精益一直是大家所提倡的思想,其中Scrum和Kanban方法作为即敏捷又精益的典型代表,许多PM都在研究,笔者近期也在学习和实施Sc ...
- C# 密封
到目前位置所说的都是让类如何如何进行继承啊 ,重写啊,巴不得类有十多个继承,超级多的重写. 但是,今天我们来说说不允许继承和不允许重写! 这个不允许继承是包括类和方法. 这种情况好比: 爸爸有私房钱, ...
- 趣图:向客户介绍的产品VS实际开发的产品
趣图:客户需求 vs 最终产品 趣图:你永远想不到用户怎么使用你的产品
- HDU6299-2018ACM暑假多校联合训练1002-Balanced Sequence
这个题的题意是给你n个字符串,认定()是一种平衡的串,两个以上连续的()()也是一种平衡的串,如果一对括号里面包含一个平衡的串,这个括号也被算在这个平衡的串之内, 如(()(()))是一个长度为8的平 ...
- Web Service入门
[IT168 技术文档] 一.什么是Web Service? Web Service是构建互联网分布式系统的基本部件.Web Services 正成为企业应用集成(Enterprise App ...
- SDUT OJ 数据结构实验之图论四:迷宫探索
数据结构实验之图论四:迷宫探索 Time Limit: 1000 ms Memory Limit: 65536 KiB Submit Statistic Discuss Problem Descrip ...
- APUE第八章-进程控制
一.进程标识 二.函数fork 1.写时复制,copy-on-write 2.文件共享,父进程等待子进程完成,子进程结束后,它对任一共享描述符的读写操作的文件偏移量已做相应的更新,同时操作时,可以考虑 ...
- redux超易学三篇之三(一个逻辑完整的react-redux)
配合源代码学习吧~ : 我是源代码 这一分支讲的是 如何完整地(不包含优化,也没有好看的页面) 搭建一个 增删改查 的 react-redux 系统 不同于上一节的 react-redux,这里主要采 ...
- <!-- -->是HTML的注释标签js,css注释
<!-- -->是HTML的注释标签 js,css:单行注释以 // 开头. 多行注释以 /* 开始,以 */ 结尾. web大作业(Vip视频解析) <!-- 这个网页是vip视频 ...