程序健壮性: 正常、错误和异常
准确而细致的错误处理是确保程序健壮性的重要关卡。 --------- 引
很容易分清正常和错误的区别,但不容易分辨错误和异常的区别。 事实上,从事情概率上可以很容易地分辨。 无论是正常、错误或异常,都是事情发生的一种可能,只是通常正常情形发生的概率更多,错误次之,异常很小。 但小概率事件也会发生。
举例来说,写一个与天气有关的程序。 天气有晴天、阴天、雨天、雪天,偶尔会有酸雨天,还有既晴又雨的时候。 晴天、阴天和雨天、雪天都可以看成正常情况, 但酸雨天就是一种错误情形,因为是不期望的;既晴且雨的天气就更少发生了,可以看作一种异常,是人们未曾预料到的。 想想最初的那个BUG, 据说是飞虫卡在机器里了, 这是一种预料之外的事件,可称为异常。 而预料之内的事情,可以包括正常和错误。
总结一下: 正常和错误都属于预料之内的事情, 发生概率较大; 异常通常是预料之外的,发生概率极小,但也可能发生。 正常与错误的区别在于,一个是预期的,一个是不期望的。
PS: 有人会问: 那么发生概率很小但却是期望的事情是什么呢? ------ 那当然是 Surprise 啦!
明确正常、错误和异常的共同点和区别对于编写健壮性好的程序是必不可少的。通常,我们更注意处理正常情况,那被看作是一种必须完成的“功能或特色”,是最起码的要求; 错误是不期望的,但也是必须处理的。异常是不曾预料的,是必须做一手防备的。 正常情形通常是程序的主控制流, 错误情形作为程序的分支, 而异常处理则作为一种高层戒备。 程序的结构基本是这样的:
String result = DEFAULT_VALUE; try { doSome1(); // 正常流 if (error) { // 错误情形的分支处理,相当于关卡 return ERROR_INFO; } return result; } catch (SomeException se) { // 异常情形 logger.error(e); return EXCEPTION_INFO; } finally { cleanup(); }
1. 初始值要认真考虑,不要随手设置。 最好设置为默认值。
2. try {} 块里面包含了正常和错误情形的处理, 正常情形是主控制流, 错误情形是分支处理(既然预料到了,就必须加以处理了)。 异常则在 try 之外, 因为是预料不到,要防一手。
3. 无论底层会出现什么错误, 高层总是会返回一个合理的值。所有的情况,包括异常也被处理了。
4. 使用卫述句式。错误情形处理总是会导致一大堆 if-else , 降低程序可读性。对比一下:
if (正常情形) { doNormal(); } VS: if(someError) { doSomeError(); return SOME_ERROR_INFO1 };
elseif(SomeError) {doSomeError();} if(someError2) { doSomeError2(); return SOME_ERROR_INFO2 };
else {doSomeError2(); } // continue normal doing.
反正我更倾向与后者呈现的结构。
5. 出错了总是会返回某种信息,交由高层处理。 总有一层能够合理处理。可以返回默认值,抛出异常,或交由更高一层, 或者什么都不做,直接忽略。 总要采取一种措施。
6. 由于错误涉及到底层到高层的传递,甚至传递不只一层, 因此, 是一个整体构架的问题。 应该从整体上仔细设计。