小课堂Week10 例外处理设计的逆袭Part3
小课堂Week10
例外处理设计的逆袭Part3
今天是《例外处理设计的逆袭》这本书阅读的第三天,也是最后一天,我们会主要通过实例,对Part2中提出的例外处理等级进行解读。
Level1
Level1的要求是立即中止运行 ,所有例外都往外抛,全部报告给使用者,或者开发者使用。
案例1
我们看一个实例,如下代码存在一些什么样的问题:
public int withdraw(int amount) {
if (amount > 100)
return -1;
else
return 100 - amount;
}
- 问题:使用返回码来表示异常,造成了返回信息的二义性,会导致上游调用复杂,同时也不能清晰表达具体错误原因。
- 解决:用异常来代替返回码。
进一步讨论下,异常名称应该如何命名:
1.ATMException
2.WithdrawException
3.NotEnoughMoneyException
以上三个中,哪一个更好?
从Java异常的定义看,一个异常是能包含一个message和一个其他异常的。
message用来表示错误的原因,cause用来传递上级的异常。
所以我们排除选项3,这个应该在message中表示。
public Throwable(String message, Throwable cause) {
fillInStackTrace();
detailMessage = message;
this.cause = cause;
}
对于选项1和选项2,都表示了异常的来源,都是可以的,这个要看我们具体模块划分的粒度,如果是一个银行系统,那么ATM这个粒度是合适的,而如果是ATM机的系统,withdraw这个粒度是合适的。
最后还要说明一点的就是,自定义异常,建议定义为unchecked exception,这样就对上游代码没有侵入,可以比较顺利的达到传递到最外层的Level1目标。
修改后如下:
public int withdraw(int amount) {
if (amount > 100)
throw new ATMException("余额不足");
else
return 100 - amount;
}
案例2
看下下面这段代码有没有什么问题:
public void close(AutoCloseable res) throws Exception {
try {
res.close();
} catch (Exception e) {
logger.error("关闭资源错误", e);
throw new Exception("关闭资源信息", e);
}
}
- 问题:在catch段中,同时记录了日志和抛出异常
- 解决:根据实际场景,二选一。
何时抛出异常呢,一般是在非外层的模块中,如案例1所示情况。
何时记日志呢,有两种场景:
- 第一是在finnally的场景中,不建议抛出,因为此类清理操作的异常会覆盖掉正常的异常情况。而案例中的代码,也是一个清理操作,是和finnally中代码等价的。所以只要记录日志即可。
public void close(AutoCloseable res) throws Exception {
try {
res.close();
} catch (Exception e) {
logger.error("关闭资源错误", e);
}
}
- 第二是在程序的最外层,这个时候要统一记录日志,但需要注意的是,Exception并不能覆盖Java中的所有的异常,从全面性角度看,我们应该捕获Throwable来记录日志。
public static void main(String[] args) {
try {
//do something
} catch (Throwable e) {
logger.error("关闭资源错误", e);
}
}
Level2
和Level1相同,Level2中异常都往外报,但在错误发生时,故障程序是可以继续运行的。这里主要讨论下在Level2会用到的一些模式:
考虑如下代码逻辑,如何可以在某步执行异常时,确保程序继续运行?
public void runBatchJob(List<Integer> numList) {
int temp = 100;
for (Integer num : numList) {
temp = temp / num;
}
}
我们可以引入一个checkpoint对象,包含establish、recover、drop三个方法。
class NumCheckpoint{
private Integer checkpointedNum;
public void establish(Integer backupNum) {
//备份数据
this.checkpointedNum = backupNum;
}
public Integer recover() {
// 将备份数据还原
return this.checkpointedNum;
}
public void drop() {
// 删除备份资料
}
}
然后在代码的try..catch...finnally块中分别使用,实现异常的恢复
public void runBatchJob(List<Integer> numList) {
//Checkpoint对象
NumCheckpoint numCheckpoint = new NumCheckpoint();
Integer temp = 100;
for (Integer num : numList) {
try {
//establish()
numCheckpoint.establish(temp);
temp = temp / num;
} catch (Exception e) {
//recover()
temp = numCheckpoint.recover();
} finally {
//drop()
numCheckpoint.drop();
}
}
}
此外,我们可以引入一个ErrorCollector来收集异常
public interface ErrorCollector {
List<Exception> getErrors();
void addError(Exception error);
void clear();
int size();
}
public void runBatchJob(List<Integer> numList, ErrorCollector errorCollector) {
//Checkpoint对象
NumCheckpoint numCheckpoint = new NumCheckpoint();
Integer temp = 100;
for (Integer num : numList) {
try {
//establish()
numCheckpoint.establish(temp);
temp = temp / num;
} catch (Exception e) {
//recover()
temp = numCheckpoint.recover();
errorCollector.addError(e);
} finally {
//drop()
numCheckpoint.drop();
}
}
}
}
Level3
Level3是要求在Level2的基础上,提供应急处理方法,确保业务能正确执行。
我们来看一下案例:
public String readUser(String name) {
try {
return readFromDatabase(name);
} catch (Exception e) {
try {
return readFromRedis(name);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
这段代码实现了Level3的向前恢复的要求,当从数据库获取失败时,从redis来取数,从功能上看是可以的。
但从代码结构看不太好。因为其将一部分的处理放在了catch段中,出现了异常嵌套的情况,这个是异常处理代码的一个禁忌。
所以我们做一下改进,使用循环来替代异常嵌套,逻辑看起来更清晰,而且只需要通过调整循环中的变量,就能实现retry的策略。
public String readUser(String name) {
int attempt = 1;
while (true) {
try {
if (attempt <= 1) {
return readFromDatabase(name);
}
if (attempt == 2) {
return readFromRedis(name);
}
} catch (Exception ex) {
attempt++;
if (attempt > 3) {
throw new RuntimeException(ex);
}
}
}
}
小结
关于《例外处理设计的逆袭》这本书的介绍就到这里结束了,主要是讲了一些我认为比较重要的点,书中还有其他很多精彩的部分也欢迎大家去阅读。最后的最后,这本书的实战性是很强的,所以请大家在工作中多动手、多实践,只有这样才能把知识变成自己的技能。
小课堂Week10 例外处理设计的逆袭Part3的更多相关文章
- 小课堂Week9 例外处理设计的逆袭Part2
小课堂Week9 例外处理设计的逆袭Part2 今天继续阅读<例外处理设计的逆袭>这本书,我们先看两个案例: 案例1 问:如果要设计一个依据学号到数据库中查询学生资料的函数,当找不到符合条 ...
- 小课堂Week8 例外处理设计的逆袭Part1
小课堂Week8 例外处理设计的逆袭Part1 今天和大家讲一本书,书名是<例外处理设计的逆袭>. 为什么想讲这本书,是因为,例外处理在程序代码中到处存在,但是这些到底该如何写好,总觉得有 ...
- Spark小课堂Week7 从Spark中一个例子看面向对象设计
Spark小课堂Week7 从Spark中一个例子看面向对象设计 今天我们讨论了个问题,来设计一个Spark中的常用功能. 功能描述:数据源是一切处理的源头,这次要实现下加载数据源的方法load() ...
- Spark小课堂Week2 Hello Streaming
Spark小课堂Week2 Hello Streaming 我们是怎么进行数据处理的? 批量方式处理 目前最常采用的是批量方式处理,指非工作时间运行,定时或者事件触发.这种方式的好处是逻辑简单,不影响 ...
- Spark小课堂Week1 Hello Spark
Spark小课堂Week1 Hello Spark 看到Spark这个词,你的第一印象是什么? 这是一朵"火花",官方的定义是Spark是一个高速的.通用的.分布式计算系统!!! ...
- 小课堂week15 年终小结
年终小结 一年的最后,想和大家回顾一下今年讲过的技术和书,用一些问答,一起来提炼一下精华. Spark 为什么需要分布式计算? 计算的增长速度超过了硬件的增长,单一服务器无法负荷.多服务器带来的是复杂 ...
- 小课堂Week12 Clean Code Part1
小课堂Week12 Clean Code Part1 今天的主题是函数,让我们看一个函数,找一找其中的"不整洁". 我们也根据这段代码,讨论下对于整洁代码的两个重要原则. publ ...
- 小课堂Week11 会说话的代码
小课堂Week11 会说话的代码 今天主要讨论下,在编码过程中和"命名"相关的问题.因为命名方法比较自由,如果要提高可读性,我们需要尽量使其符合正规的英文语法习惯. 变量/属性 通 ...
- Spark小课堂Week6 启动日志详解
Spark小课堂Week6 启动日志详解 作为分布式系统,Spark程序是非常难以使用传统方法来进行调试的,所以我们主要的武器是日志,今天会对启动日志进行一下详解. 日志详解 今天主要遍历下Strea ...
随机推荐
- Unity3D 4.61 实现淡入淡出的场景过渡方法。
还在学习过程中,如果有大大看到请指点. orz原来官方就有了更好的处理方法的教程,具体查看下面视屏. [Unity官方实例教程 秘密行动] Unity官方教程<秘密行动>(五) 屏幕渐变效 ...
- 【Android 界面效果19】Android中shape的使用
Android中常常使用shape来定义控件的一些显示属性,今天看了一些shape的使用,对shape有了大体的了解,稍作总结: 先看下面的代码: <shape> ...
- leetcode 题解 Add Two Numbers(两个单链表求和)
题目: You are given two linked lists representing two non-negative numbers. The digits are stored in r ...
- Kinect For Windows V2开发日志七:照片合成与背景消除
上一篇里讲到了Kinect可以从环境中区分出人体来.因此可以利用这个功能,来把摄像头前的人合成进照片里,和利用Photoshop不同的是,这样合成进去的人是动态且实时的. 简单的思路 BodyInde ...
- noi 97 积木游戏
思路:黑书的例题 #include<map> #include<set> #include<cmath> #include<queue> #includ ...
- 深入浅出ExtJS 第五章 树形结构
5.1 TreePanel的基本使用 //树是一种非常典型的数据结构; 5.1.1 创建一棵树 //树控件有Ext.tree.TreePanel类定义,控件的名称为TreePanel;TreePane ...
- Slickflow.NET 开源工作流引擎基础介绍(二) -- 引擎组件和业务模块的交互
集成流程引擎的必要性 业务过程的变化是在BPM系统中常见的现象,企业管理层需要不断优化组织架构,改造业务流程,不可避免地带来了业务流程的变化,企业信息系统就会随之面临重构的可能性.一种直接的方式是改造 ...
- 【转载】Dubbo与Zookeeper、SpringMVC整合和使用(负载均衡、容错)
http://blog.csdn.net/congcong68/article/details/41113239 互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及 ...
- android app性能优化大汇总(内存性能优化)
转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 写在最前: 本文的思路主要借鉴了2014年AnDevCon开发者大会的一个演讲PPT,加上 ...
- android APP是否需要缓存?+简单架构
问题的由来 昨天,当我写完我的第一篇博客之后,我便百无聊赖的玩起了手机!当我打开Google自带的一些app的时候,发现他们直接叫我连接网络,并没有缓存上次从网络获取的数据.这就让我感到很奇怪!于是我 ...