作者总结自己多年面试他人以及被他人面试的经验,发现应聘者可以从代码的规范性、完整性和鲁棒性三个方面提高代码的质量。
程序员在职业生涯中难免要接受编程面试。有些程序员由于平时没有养成良好的编程习惯,在面试时写出的代码质量不高,最终遗憾地与心仪的公司和职位失之交臂。因此,如何在面试时能写出高质量的代码,是很多程序员关心的问题。
代码的规范性
面试官是根据应聘者写出的代码来决定是否录用一个应聘者的。应聘者首先要把代码写得规范,才可以避免很多低级错误。如果代码写得不够规范,会影响面试官阅读代码的兴致,至少印象分会打折扣。书写、布局和命名都决定着代码的规范性。
规范的代码书写清晰。绝大部分面试都要求应聘者在白纸或者白板上书写。由于现代人已经习惯了敲键盘打字,手写变得越发不习惯,因此写出来的字潦草难辨。虽然应聘者没有必要为了面试特意去练字,但在面试过程中减慢写字速度、尽量把每个字母写清楚还是很有必要的。不用担心没有时间去写代码。通常编程面试的代码量都不会超过50行,书写不用花多少时间,关键是在写代码之前形成清晰的思路并能把思路用编程语言清楚地书写出来。
规范的代码布局清晰。平时程序员在集成开发环境如Visual Studio里面写代码,依靠专业工具调整代码的布局,加入合理的缩进并让括号对齐成对呈现。离开这些工具,应聘者就要格外注意布局问题。当循环、判断较多逻辑较复杂时,缩进的层次可能比较多。如果布局不够清晰,缩进也不能体现体现代码的逻辑,这样的代码将会让人头晕脑胀。
规范的代码命名合理。很多初学编程的人在写代码时总是习惯用最简单的名字来命名,变量名是i、j、k,函数名是 f、g、h。由于这样的名字不能告诉读者对应的变量或者函数的意义,代码一长就会变得非常晦涩难懂。强烈建议应聘者在写代码时,用完整的英文单词组合命名变量和函数,比如函数需要传入一个二叉树的根结点作为参数,则可以把该参数命名为BinaryTreeNode* pRoot。不要因为这样会多写几个字母而觉得麻烦。如果一眼能看出变量、函数的用途,应聘者就能避免自己搞混淆而犯一些低级的错误。同时合理的命名也能让面试官一眼就能读懂代码的意图,而不是让他去猜变量到底是数组中的最大值还是最小值。
代码的完整性
在面试的过程中,面试官会非常关注应聘者考虑问题是否周全。面试官通过检查代码是否完整来考查应聘者的思维是否全面。通常面试官会检查应聘者的代码是否完成了基本功能、输入边界值是否能得到正确的输出、是否对各种不合规范的非法输入做出了合理的错误处理。
应聘者在写代码之前,首先要把可能的输入都想清楚,从而避免在程序中出现各种各样的质量漏洞。也就是说在编码之前要考虑单元测试。如果能够设计全面的单元测试用例并在代码中体现出来,那么写出的代码自然也就是完整正确的了。通常程序员可以从功能测试、边界测试和负面测试三方面设计测试用例,以确保代码的完整性。
首先要考虑的普通功能测试的测试用例。应聘者首先要保证写出的代码能够完成面试官要求的基本功能。比如面试题要求完成的功能是把字符串转换成整数,应聘者就可以考虑输入字符串“123”来测试自己写的代码。这里要把零、正数(比如123)和负数(比如-123)都考虑进去。
考虑功能测试时,应聘者要尽量突破常规思维的限制,避免忽视某些隐含的功能需求。比如“打印从1到最大的n位数”,很多人觉得很简单。最大的3位数是999、最大的4位数是9999。这些数字很容易就能算出来。但最大的n位数都能用int型表示吗?如果超出int的范围可以考虑long long类型。超出long long能够表示的范围呢?面试官是不是要求考虑任意大的数字?如果面试官确认题目要求的是任意大的数字,那么这个题目就是一个大数问题。此时需要特殊的数据结构来表示数字,比如用字符串或者数组来表示大的数字,才能确保不会溢出。
其次需要考虑各种边界值的测试用例。很多代码都包含有循环或者递归。如果代码是基于循环,那么结束循环的边界条件是否正确?基于循环的代码要特别注意开区间和闭区间的使用(也就是区分<与<=、>与>=)。如果代码是基于递归,递归终止的边界值是否正确?这些都是边界测试时要考虑的用例。还是以字符串转换成整数的问题为例,应聘者写出的代码应该确保能够正确转换最大的正整数和最小的负整数。
再次还需要考虑各种可能的错误的输入,也就是负面测试的测试用例。应聘者写出的函数除了要顺利地完成要求的功能之外,当输入不符合要求时,面试官还希望他能做出合理的错误处理。在设计把字符串转换成整数的函数时,应聘者就要考虑当输入的字符串不是一个数字,比如“1a2b3c”,怎么告诉函数的调用者这个输入是非法的。
前面讨论的都是要全面考虑当前需求对应的各种可能输入。在软件开发过程中,永远不变的就是需求会一直改变。如果应聘者在面试时写出的代码能够把将来需求可能的变化都考虑进去,在需求发生变化时能够尽量减少代码改动的风险,那他就向面试官展示了自己对程序可扩展性和可维护性的理解,必定能得到面试官的青睐。如果应聘者在解答面试题“调整数组顺序使奇数位于偶数前面”时能够考虑可扩展性,他写出的代码不仅仅只是解决调整奇数和偶数的问题,还能考虑到把调整数字顺序的功能和判断一个数字是奇数还是偶数的功能解耦。这样当今后需求功能扩展要求解决类似的问题,比如调整负数和非负数的顺序、调整能被3整除的数字和不能被3整除的数字的顺序,只需要添加很少的代码都能做到,于是提高了代码的可扩展性和可维护性。
三种错误处理的方法
通常有三种方式把错误信息传递给函数调用者。
函数用返回值来告知调用者是否出错。比如很多Windows的API就是这个类型。Windows中很多API的返回值为0表示API调用成功,而返回值不为0表示在API调用的过程中出错了。微软为不同的非零返回值定义了不同的意义,调用者可以根据这些返回值判断出错的原因。这种方式最大的问题是使用不便,因为函数不能直接把计算结果通过返回值直接赋值给其他变量,同时也不能把这个函数计算的结果直接作为参数传递给其他函数。
原文转自:http://www.kuqin.com/job/20120210/318003.html