开发实践思考(二)

本篇接上一篇《开发实践思考(一)》,继续将之前散落的思考点汇总,每篇10条。

1. 同层级的判断只在当前层级生效,不能跨层级保证

举一个简单的例子:


bool Fun1(const char* pData)
{
	if (NULL == pData)
	{
		assert(0);
		return false;
	}

	// XXXXXX处理

	DetailFun(pData);
}

bool DetailFun(const char* pData)
{

}


提问,在DetailFun函数中,需不需要对pData再做非空判断?
观点A:外层调用者已经保证pData的有效性,DetailFun内部无需再次处理。
观点B:DetailFun函数内部对入参也要做有效性判断,因为不能保证它被其他函数调用时,其他函数也做了同样保证。

笔者的看法:同层级的判断,只能保证同层级的有效性,一旦将数据传给其他模块,合法性就交给其他模块来鉴别,而不要自身在多做一层判断,如果有效就传给其他模块,如果无效就不传。

小结:每个函数只需要关注自身入参的有效性,要做到,无论在什么环境,都不会因为外部入参的错误而导致自身出错。

2. 不要被别人写的代码误导

别人写的不一定是最好的,如果只是简单的仿照继承,那无助于成长。

在平时工作中,免不了维护旧代码。对旧代码有两种态度:

态度1:旧代码跑了这么旧,它这样写,猜测应该是有它的意图,可能是应对某种特殊情况之类的吧,目前不知道,因此,新的实现照搬原来的写法。

态度2:在仔细分析旧代码流程发现,原来的写法是暂时性的解决方案,有一股“弄巧成拙”的碰巧感和“短平快”的紧急解决方案。

笔者的看法偏向于态度2,很多时候,面对问题,通常有两种处理方向:

  1. “短平快”的解决方案,一般改动范围小,加个else/case,加个虚函数重写之类的,能屏蔽或绕过问题的触发条件,使其缓解,但通常不优雅,加大耦合。

  2. “彻底的”的解决方案,从问题发生点逐层上推,直到问题根源,一路顺着调用关系链条来彻底改正。同时,检查工程中其他类似的地方,将同类型的问题一并解决掉。

这两种处理方向都有各自的使用场景。从长远来看,应尽量少一些特例、特殊处理,更多的是通用问题的通用方法。如果当时迫不得已那样做,后续也要标记下,用更为通用、优雅的方法来解决。

3. 代码提交保持单一职责

每次提交只保证做一件事,提交的所有修改都聚焦于这件事。根据自己的经验,可将代码提交按照目的分为以下三类:

  1. 改Bug
  2. 实现新功能
  3. 代码重构

在提交代码时,针对以上三类目的,要有明确区分,尽量不要混在一起提交。

有时候,场景1和3可以一并提交,但要限制重构范围,重构与此Bug密切相关的地方,那些不相关的重构,不要混杂在一起。

4. 程序正确性问题

从测试的角度来说,要证明程序有问题,很简单,构造各种场景,看实际输出与预期输出是否一致,只要不一致,那就说明程序是有问题的。

从开发的角度来说,如果想向别人证明,自己的程序是很少问题甚至是没有问题,是非常难的。

因为从逻辑上来看,这个类似于有罪判定。默认情况下,人都是无罪的(类似于程序都是正确的),谁怀疑他有罪,谁就提供证据,将举证责任交给提出假设的一方是惯例。

还有一点,从逻辑上说,证有不证无。你只能证明某一个人有罪,无法证明一个人无罪。

回到开发上看,开发自测时,只能尽可能测试所有能想到的可能场景,在已列出出的场景范围内,可证明是没问题的。测试要做的是穷举尽可能多的场景,至于到底有多少个场景,有多少个路径,每个人的经验、看法不同,难以达成一致。

开发和测试能做到的,在自己能力范围内,尽可能构造场景并发现错误,但无法杜绝问题的产生。

5. 枚举定义以及辅助函数定义在一起

定义枚举时,建议将对应的辅助抓换函数写在一起,这样的好处是减少后续新增定义时,忘记在转换函数中添加对应入口造成的潜在的错误。

6. 显式送参优于隐式

当接口文档中有这样描述入参,“送空和送0是一样效果”,那建议显式的送0,而不是送空。这样做有如下好处:

  1. 清晰直接
  2. 如果后续结构有新增入参,那么之前的入参可以一一对应地上,而不靠着隐含的空字符串

此条同样适用于函数默认参数,笔者觉的,函数默认参数会加大调用者的心智负担,在传参数时,显式送参优于默认参数,少用默认参数。

7. 谨慎处理特殊值

针对"0"、""、" "和"-1(无效值)"等特殊数值,在业务判断中要特别处于,要明确区分上述情况下的处理。

8. 提交Bug时关联类似Bug

在解决Bug时,如果感觉之前有解决过类似Bug,这种情况,可以在此Bug的备注中,注明与之关联的Bug号,提醒测试在测试时注意下。

如果此BugA是因为修改另一个BugB导致的,那更需要在备注中,将BugB的改动导致BugA的修改简要说明下,有助于完整描述BugA的生命周期。

9. 会议的会后总结

凡是进行会议,无论是电话会议还是现场会议,在大家讨论过后,需要有会议记录员记录在会议上的决定,记录哪些问题是在会议上确定的,哪些是待定的,哪些是新增的,这些由会议记录员来记录,并将上述事项发给参会人员以及那些无法参会但有重要关联的人员,同时@相关人员,将会议上已确定的事项进行分派,便于问题推动以及跟进。

10. 沟通转述问题

在答复测试的疑问,有些点需要向其他人员进一步询问再答复给测试。在得到其他人员的回复后,在转述会给测试时,不要简简单单的截一个与其他人员的沟通截图,让测试自己去揣摩结果,也不要将其他无关的沟通细节回复给测试,应当提炼与此问题紧密相关的结论,并以简洁精炼的方式来回复。

在给别人传输文档时,除了文档名称要见名知意外,还要附加一句简短的文档说明。

在传给别人图片时,较好的操作是在图片中添加说明,以画框、高亮、简洁文字说明等方式都。

为了防止沟通中的理解偏差,可以将别人给的答复,用自己的话转述一片,让别人再次确认,类似于TCP的三次握手中的第三次确认过程,达到对该问题的一致理解。

posted @ 2021-08-11 16:47  浩天之家  阅读(50)  评论(0编辑  收藏  举报