2012年(366)
分类: 系统运维
2012-03-03 13:34:51
程序员在职业生涯中难免要接受编程面试。有些程序员由于平时没有养成良好的编程习惯,在面试时写出的代码质量不高,最终遗憾地与心仪的公司和职位失之交臂。因此,如何在面试时能写出高质量的代码,是很多程序员关心的问题。
代码的规范性
面试官是根据应聘者写出的代码来决定是否录用一个应聘者的。应聘者首先要把代码写得规范,才可以避免很多低级错误。如果代码写得不够规范,会影响面试官阅读代码的兴致,至少印象分会打折扣。书写、布局和命名都决定着代码的规范性。
规范的代码书写清晰。绝大部分面试都要求应聘者在白纸或者白板上书写。由于现代人已经习惯了敲键盘打字,手写变得越发不习惯,因此写出来的字潦草难辨。虽然应聘者没有必要为了面试特意去练字,但在面试过程中减慢写字速度、尽量把每个字母写清楚还是很有必要的。不用担心没有时间去写代码。通常编程面试的代码量都不会超过 50 行,书写不用花多少时间,关键是在写代码之前形成清晰的思路并能把思路用编程语言清楚地书写出来。
规范的代码布局清晰。平时程序员在集成开发环境如 Visual Studio 里面写代码,依靠专业工具调整代码的布局,加入合理的缩进并让括号对齐成对呈现。离开这些工具,应聘者就要格外注意布局问题。当循环、判断较多逻辑较复杂时,缩进的层次可能比较多。如果布局不够清晰,缩进也不能体现体现代码的逻辑,这样的代码将会让人头晕脑胀。
规范的代码命名合理。很多初学编程的人在写代码时总是习惯用最简单的名字来命名,变量名是i、j、k,函数名是f、g、h。由于这样的名字不能告诉读者对应的变量或者函数的意义,代码一长就会变得非常晦涩难懂。强烈建议应聘者在写代码时,用完整的英文单词组合命名变量和函数,比如函数需要传入一个二叉树的根结点作为参数,则可以把该参数命名为 BinaryTreeNode* pRoot。不要因为这样会多写几个字母而觉得麻烦。如果一眼能看出变量、函数的用途,应聘者就能避免自己搞混淆而犯一些低级的错误。同时合理的命名也能让面试官一眼就能读懂代码的意图,而不是让他去猜变量到底是数组中的最大值还是最小值。
代码的完整性
在面试的过程中,面试官会非常关注应聘者考虑问题是否周全。面试官通过检查代码是否完整来考查应聘者的思维是否全面。通常面试官会检查应聘者的代码是否完成了基本功能、输入边界值是否能得到正确的输出、是否对各种不合规范的非法输入做出了合理的错误处理。
三种测试用例确保代码的完整性
应聘者在写代码之前,首先要把可能的输入都想清楚,从而避免在程序中出现各种各样的质量漏洞。也就是说在编码之前要考虑单元测试。如果能够设计全面的单元测试用例并在代码中体现出来,那么写出的代码自然也就是完整正确的了。通常程序员可以从功能测试、边界测试和负面测试三方面设计测试用例,以确保代码的完整性。
考虑功能测试时,应聘者要尽量突破常规思维的限制,避免忽视某些隐含的功能需求。比如“打印从 1 到最大的n位数”,很多人觉得很简单。最大的 3 位数是 999、最大的 4 位数是 9999。这些数字很容易就能算出来。但最大的n位数都能用 int 型表示吗?如果超出 int 的范围可以考虑 long long 类型。超出 long long 能够表示的范围呢?面试官是不是要求考虑任意大的数字?如果面试官确认题目要求的是任意大的数字,那么这个题目就是一个大数问题。此时需要特殊的数据结构来表示数字,比如用字符串或者数组来表示大的数字,才能确保不会溢出。
前面讨论的都是要全面考虑当前需求对应的各种可能输入。在软件开发过程中,永远不变的就是需求会一直改变。如果应聘者在面试时写出的代码能够把将来需求可能的变化都考虑进去,在需求发生变化时能够尽量减少代码改动的风险,那他就向面试官展示了自己对程序可扩展性和可维护性的理解,必定能得到面试官的青睐。如果应聘者在解答面试题“调整数组顺序使奇数位于偶数前面”时能够考虑可扩展性,他写出的代码不仅仅只是解决调整奇数和偶数的问题,还能考虑到把调整数字顺序的功能和判断一个数字是奇数还是偶数的功能解耦。这样当今后需求功能扩展要求解决类似的问题,比如调整负数和非负数的顺序、调整能被 3 整除的数字和不能被 3 整除的数字的顺序,只需要添加很少的代码都能做到,于是提高了代码的可扩展性和可维护性。
三种错误处理的方法
通常有三种方式把错误信息传递给函数调用者。
上述三种错误处理的方式各有优缺点。那么面试时应聘者该采用哪种方式呢?这要看面试官的需求。在听到面试官的题目之后,应聘者要尽快分析出可能存在哪些非法输入,并和面试官讨论该如何处理这些非法输入。和面试官进行这样的讨论对应聘者是有益的,因为面试官会觉得他对错误处理有着全面的了解,并且还会觉得他有很好的沟通能力。
代码的鲁棒性
鲁棒性是指程序能够判断输入是否合乎规范要求,并对不合要求的输入予以合理的处理。容错性是鲁棒性的一个重要体现。不鲁棒的软件在发生异常事件时,比如用户输入错误的用户名、试图打开的文件不存在或者网络不能连接,就会出现不可预见的诡异行为,或者干脆整个软件崩溃。这样的软件对于用户而言,不亚于一场灾难。
由于鲁棒性对软件开发非常重要,面试官在招聘时对应聘者写出的代码是否鲁棒也非常关注。提高代码的鲁棒性的有效途径是进行防御性编程。防御性编程是一种编程习惯,是指预见在什么地方可能会出现问题,并为这些可能出现的问题制定处理方式。
在面试时,最简单也最实用的防御性编程就是在函数入口添加代码以验证用户输入是否符合要求。通常面试要求的是写一两个函数,应聘者需要格外关注这些函数的输入参数。如果输入的是一个指针,那指针是空指针怎么办?如果输入的是一个字符串,那么字符串的内容为空怎么办?如果应聘者能把这些问题都提前考虑到,并作相应的处理,那么面试官就会觉得他有防御性编程的习惯,能够写出鲁棒的软件。
当然并不是所有与鲁棒性相关的问题都只是检查输入的参数这么简单。应聘者看到问题时,要多问几个“如果不……那么……”这样的问题。比如面试题“链表中倒数第k个结点”,这里隐含着一个条件就是链表中结点的个数大于k。应聘者就要问自己如果链表中的结点不是大于k个,那么代码会出什么问题?这样的思考方式,能够帮助发现潜在的问题并提前解决问题。这比事后让面试官发现问题之后应聘者再去慌忙分析代码查找问题的根源要好很多。
小结
本文从规范性、完整性和鲁棒性三方面介绍了应聘者如何在面试时写出高质量代码(如下图所示)。
第一,应聘者在白纸或者白板上手写代码时要注意规范性,尽量清晰地书写每个字母,通过缩进和对齐括号让代码布局合理,同时还要合理命名代码中的变量和函数。第二,应聘者最好在编码之前全面考虑所有可能的输入,确保写出的代码在完成了基本功能之外,还考虑了边界条件,并做好了错误处理。只有全面考虑到这三方面的代码才是完整的代码。第三,应聘者要重视代码的鲁棒性,确保自己写出的程序不会轻易崩溃。平时在写代码时,应聘者最好养成防御式编程的习惯,在函数入口判断输入是否有效并对各种无效输入做好相应的处理。应聘者如果能够做到这三点,自然就能写出高质量的代码,最终通过面试拿到 Offer 也将是水到渠成的事情。
作者何海涛,思科高级软件工程师,之前先后任职于 Autodesk 和微软。主要关注C++/C#的开发技术,并对设计模式和项目管理也很感兴趣。