这第一步很重要,而且实践难度其实不高。对于有“注释驱动编码”习惯的开发人员来说更是容易。
对示例代码段一,“翻译”成简略的“注释”(有省略),可以如下文(以大括号{之后紧接的为第一行):
//(1-5行)初始化输出值 //(6-102行)超大循环,遍历输入的mnsInfos,对每一项执行以下的操作: //(8-14行)首先从mnsInfos中取出一组数据并记录其中的所有怪物ID //(15-21行)如果该组数据为空,则记入输出参数vmnsInfos并跳过 //(22-25行)(如果该组数据不为空)根据这一组怪物ID获取其对应的怪物类型(mnsTypes)及无效怪物的集合 //(26-95行)如果存在部落群和怪物不一致或者怪物类型错误的情况,那么: // a、(29-42行)若一个有效怪物都没有时,记录无效怪物 // b、(43-94行)否则遍历这一组所有怪物,并分别记录下有效值和无效值,当 //1、数据库中存在怪物信息且类型也有效时,记为有效怪物,并存入输出参数vmnsInfos //2、怪物ID和对应的怪物类型不一致或怪物不属于部落群,记为无效怪物,并存入输出参数ivmnsInfos //(96-101行)否则如果部落群和怪物一致,且对应的怪物类型也一致,则将信息都存入vmnsInfos //(103-105行)循环结束,返回执行成功标识 |
从以上的“注释”中,可以看出几点:
1、虽然该函数有100多行,但是其实逻辑并没有那么复杂。
2、逻辑并不复杂的代码却写出100多行,其中大量代码都是在做基本的sequence、vector遍历、拷贝的原始动作,这些完全可以剥离出去,使主体逻辑清晰化。
2、返回值不能指示任何情况,因为只要函数执行完就必定返回true;如果中途抛出异常,该函数也没处理,自然也不会有返回值。该设计不是很合理。
如果有足够的把握,上面的“翻译”过程也可以在脑中进行,无需写出来。
当代码“翻译”为“注释”后,就可将提取出的逻辑与设计和需求进行比对,检查其是否忠实体现了设计,是否完整、准确地实现了需求。
需求和设计文档这里就不给出了,对抽取出的逻辑参考相关文档进行检查不是太困难的事,在此不展开。
第二步:
接下来要做的就是针对“常规输入”的可能取值进行核查。所谓“常规输入”就是指函数的参数,这是函数的显式输入。
我们知道,任何一段程序都可以用IPO(Input-Process-Output)模型来分析、检查。所以代码检视要做的也是针对所有输入的可能取值,检查代码段是否有正确的处理,能否产生正确的输出。
函数参数正是最显眼明白的输入项,对它的取值采取等价类划分和取边界值,就能够检视出代码中是否能够覆盖到各类的输入组合。
示例代码段一中,函数有两个输入参数hrdId和mnsInfos(余下两个是输出参数),我们一个个地看。
hrdId在代码中仅有一处使用,即
NPCManagement::MonsterLoader::getMonsterType(hrdId, -1, monsterIds, mnsTypes); |
这是一个外部调用。
第一条、检视规则:输入参数仅用于外部调用的情况,在代码检视中可不必关注。
因为通常情况下这样的输入参数不影响被检视函数走向,而是否影响被调用的外部函数、对象,则是应当在其他的检视活动中覆盖的内容。
该规则,我们在“常规输入”检查阶段跳过这一行。