代码评审三步走

发表于:2012-7-23 10:32

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:夏江平    来源:tester

分享:

  在检视涉及“共享资源”的代码时,除了检查代码对这些“共享资源”的取值是否有合适处理外,还要进一步检查当前检视代码与其他相关代码是否对“共享资源”的竞争进行正确处理,它们对“共享资源”的依赖,是否构成“依赖环”。

  要将所有的共享依赖都梳理出来不是件简单的事,篇幅所限,这里就不展开讨论了。

  第四个层次,是对程序中所有异常也作为“非常规输入”处理。

  异常,即Exception,是程序在遭遇非预期或不能处理的状况时,用于通知外层程序或调用者出现状况的一种机制。

  从最简单的a=b/c可能发生的除零异常,到复杂的函数调用可以抛出多种类型的异常,这些都是程序中常见的情形。

  在调用函数或进行数据处理时发生的异常,都是由外部传入/返回给当前函数(被检视函数)的,因此对于当前函数而言,异常Exception自然也应当作为一种“非常规输入”进行处理。

  由于异常在代码段中通常是以“隐身”的方式存在着,除了函数声明时可能有的throw指示(有时候也没有)之外,难以寻觅它的踪影,因而它也是最容易被遗漏的“非常规输入”,应当作为一个重点的代码检视对象。

  在示例代码段二中,对cvtQuestById、appendQuestState、getQuest等方法的调用,都有可能遭遇它们抛出的异常。而从代码中也可以看到,该段程序对可能遭遇的异常确实进行了处理。

  再看示例代码段一,它的逻辑简单,看起来信心满满,没有任何异常保护。

  但是它真的不会遭遇任何异常吗?仔细检查可以看到其中有这样一条语句:

  NPCManagement::MonsterLoader::getMonsterType(hrdId, -1, monsterIds, mnsTypes);

  循着这条语句顺藤摸瓜,会看到getMonsterType方法是通过数据库查询实现的,且此处的数据库查询是通过新建数据库连接进行的。

  在其中建立数据库连接的部分(以一个DBConnection构造函数的面目出现),明明白白地写出了有可能抛出各种类型的异常。换言之,当无法获取数据库连接时,getMonsterType就会抛出异常,而示例代码段一中没有捕获该异常。

  这个未捕获的异常是否会引发未定义的问题,就完全要看调用该方法的外层调用者的处理了——生存还是死亡,这是一个问题。

  即便现存的所有调用者都注意到这点并进行了异常保护,也无法保证今后新增加的调用者也都会遵循这个“传统”。最好的措施还是自我保护。

  在进行代码检视时,对调用的外部函数、系统API等,都要仔细检查其是否可能抛出异常,并检查程序代码中对可能发生的异常,是否按照规范进行了适当处理。

  对异常的处理分为两个阶段:捕获与处置。按捕获情况可分为三类:捕获特定异常,捕获所有异常,不捕获异常。 捕获异常后的处置方法可以分为两类:1、记录而不处理(吞噬异常),程序继续流转;2、记录并中断程序运行,重新抛出异常。无论是哪一种捕获和处置方法,是否“正确”都要看是否符合业务逻辑,是否符合设计规范,不能一概而论。

  一般而言,对可能存在的异常不捕获是比较危险的。因为除非明确知道外围程序有异常保护机制,否则本程序对异常不处理会导致异常扩散,很有可能导致产品崩溃、错乱,发生无法预知的后果。

  以上就是我所提倡的“代码检视三步走”的全部内容。受个人能力和当前工作重点所限,以上给出的内容既不全面也可能存在许多错漏之处。希望能够起到抛砖引玉的作用,引出更多专家的宝贵经验。

66/6<123456
价值398元的测试课程免费赠送,填问卷领取吧!

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计

法律顾问:上海漕溪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2022
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号