status2 + res3;
return SomeResult.of(this.status1, this.status2, this.status3);
} catch (Exception e) {
// do rollback
}
}
你以为这样就会处理好数据rollback,甚至觉得这种代码优雅。但实际上doStep1~3每一个地方抛错,rollback的代码都不一样。
得这么写
void doWorkAndGetResult(/* some params*/) {
int res1, res2, res3;
try {
res1 = otherSvc1.doStep1(/* some params */);
this.status1 += res1;
} catch (Exception e) {
throw e;
}
try {
res2 = otherSvc2.doStep2(/* some params */);
this.status2 += res2;
} catch (Exception e) {
// rollback status1
this.status1 -= res1;
throw e;
}
try {
res3 = otherSvc3.doStep3(/* some params */);
this.status3 = status1 + status2 + res3;
} catch (Exception e) {
// rollback status1 & status2
this.status1 -= res1;
this.status2 -= res2;
throw e;
}
}
这才是得到正确结果的代码,在任何地方出错都能维护数据一致性。优雅吗?
看起来很丑。比go的if err != nil还丑。但要在正确性和优雅性取舍,肯定毫不犹豫选前者。作为程序员不能直接认为抛异常可解决任何问题,须学会写出有正确逻辑的程序,哪怕很难且看起来丑。
为达成高正确性,你不能总将自己大部分注意力放在“一切都OK的流程“,而把错误看作是可随便应付了事的工作或简单的相信exception可自动搞定一切。
8 总结
对错误处理要有敬畏之心:
- Java因为Checked Exception设计问题不得不避免使用
- 而Uncaughted Exception实在弱鸡,不能给程序员提供更好帮助
因此,程序员在每次抛错或者处理错误的时候都要三省吾身:
- 这个错误的处理是正确吗?
- 会让用户看到啥?
- 会不会搞乱数据?
不要以为自己抛个异常就完事了。在[编译器]不能帮上太多忙时,好好写UT来保护代码可怜的正确性。
请多写正确的代码!
本文由博客一文多发平台 OpenWrite 发布!