剑指offer-3 -高质量的代码

  • 代码质量
  • 代码的规范性
  • 代码的完整性
    • 从3方面确保代码的完整性
    • 3种错误处理的方法
    • 例子
  • 代码的鲁棒性
  • 关于高质量的代码 小结

代码质量

下面是几个面试官对代码质量的要求

(1)代码的容错能力,对一些特别的输入需要考虑异常状况,考虑资源的回收问题。。。

(2)一些基本的知识点,如double 类型的数据比较的问题。if( d1==d2 )
上述比较有问题,由于精度原因不能用等号判断两个小数是否相等。

(3)不能忽略边界的情况

(4)变量、函数命名的问题,而且解决一个具体的问题,需要有个合适的数据结构。

(5)从程序的正确性和鲁棒性两方面检验代码的质量。 关注对输入参数的检查、处理错误和异常的方式、命名方式等。

代码的规范性

面试官是根据应聘者写出的代码来决定是否录用他的。如果应聘者代码写的不够规范,影响面试官阅读代码的兴致,那面试官就会默默地减去几分。

(1)规范的代码书写清晰

写代码前形成清晰的思路并能把思路用编程语言清楚地书写出来。

(2)规范的代码布局清晰

当循环、判断较多,逻辑较复杂时,缩进的层次可能会比较多。

(3)规范的代码命名合理

建议:在写代码的时候,用完整的英文单词组合命名变量和函数。

代码的完整性

在面试的过程中,面试官会非常关注应聘者考虑问题是否周全。面试官通过检查代码是否完整来考查应聘者的思维是否全面。通常面试官会检查应聘者的代码是否完成了基本功能、输入边界值是否能得到正确的输出、是否对各种不合规范的非法输入做出了合理的错误处理。

从3方面确保代码的完整性

应聘者在写代码之前,需要把可能的输入都想清楚,从而避免在程序中出现各种各样的质量漏洞。也就是说在编码之前要考虑单元测试。如果能够设计全面的单元测试用例并在代码中体现出来,那么写出的代码自然也就是完全正确的了。

通常可以从功能测试、边界测试和负面测试三方面设计测试用例,以确保代码的完整性

(1)普通功能测试的测试用例。

要保证写出的代码能够实现基本功能

(2)边界值的测试用例

很多时候代码中会有循环或者是递归。如果代码是基于循环,那么结束循环的边界条件是否正确?如果是递归,递归终止的边界值是否正确?这些都是边界测试时要考虑的用例。

(3)考虑各种可能的错误的输入,也就是通常所说的负面测试的测试用例

前面3条都是要全面考虑当前需求对应的各种可能输入。在软件开发过程中,永远不变的就是需求会一直改变。如果我们在面试写出的代码能够把将来需求可能的变化都考虑进去,在需求发生变化的时候能够尽量减少代码改动的风险,那我们就向面试官展示了自己对程序可扩展性和可维护性的理解,通过面试就是水到渠成的事了。

3种错误处理的方法

通常我们有3中方式把错误信息传递给函数的调用者

(1)函数用返回值来告知调用者是否出错。比如很多Windows的API就是这个类型。Windows中很多API的返回值为0表示API调用成功,而返回值不为0表示在API调用的过程中出错了。微软为不同的非零返回值定义了不同的意义,调用者可以根据这些返回值判断出错的原因。这种方式最大的问题是使用不便,因为函数不能直接把计算结果通过返回值赋值给其他变量,同时也不能把这个函数计算的结果直接作为参数传递给其他函数。

(2)当发生错误时设置一个全局变量。此时可以在返回值中传递计算结果。这种方法比第一种方法使用起来更加方便,因为调用者可以直接把返回值赋值给其他变量或者作为参数传递给其他函数。Windows的很多API运行出错之后,也会设置一个全局变量。可以通过调用函数GetLastError分析这个表示错误的全局变量,从而得知出错的原因。

但这个方法有个问题:调用者很容易就会忘记去检查全局变量,因此在调用出错的时候忘记做相应的错误处理,从而留下安全隐患。

(3)异常
当函数运行出错的似乎,就跑出一个异常,还可以根据不同的出错原因定义不同的异常类型。因此函数的调用者根据异常的类型就能知道出错的原因,从而作相应的处理。另外,我们能显示划分程序正常运行的代码块(try模块)和处理异常的代码块(catch模块),逻辑比较清晰。

剑指offer-3 -高质量的代码_第1张图片

例子

对于: 面试11:数值的整数次方(对错误的处理)

除了对正常数据的处理,还考虑到对一些特殊数据,异常值进行操作。

输入的指数为零和负数时怎么办?
针对负数的解决方案,是否有更高效的解决方法?

面试12:打印1到最大的n位数

面试13:O(1)时间内删除链表结点

面试14:调整数组顺序使奇数位于偶数前面

代码的鲁棒性

鲁棒性(Robust),有时也翻译成健壮性。所谓鲁棒性是指程序能够判断输入是否合乎规范要求,并对不合要求的输入予以合理的处理。

容错性是鲁棒性的一个重要体现。不鲁棒性的软件在发生异常事件的时候,比如用户输入错误的用户名、试图打开的文件不存在或者网络不能连接,就会出现不可预见的诡异行为,或者干脆整个软件崩溃。这样的软件对于用户而言,不亚于一场灾难。

由于鲁棒性对软件开发非常重要,面试官在招聘的时候对应聘者写出的代码是否具有鲁棒性也非常关注。提高代码的鲁棒性的有效途径是进行防御性变成。防御性编程是一种编程习惯,是指遇见在什么地方可能会出现问题,并为这些可能出现的问题制定处理方式。比如试图打开文件时发现文件不存在,我们可以提示用户检查文件名和路径;当服务器连接不上时,我们可以试图连接备用服务器等。这样异常发生时,软件的行为也尽在我们的掌握之中,而不至于出现不可预见的事情。

在面试时,最简单也是最使用的防御性编程就是在函数入口添加代码以验证用户输入是否符合要求。通常面试要求写一两个函数,我们需要格外关注这些函数的输入参数。如果输入的是一个指针,那指针时空指针怎么办?如果输入的是一个字符串,那么字符串的内容为空怎么办?如果能把这些问题都提前考虑到,并做相应的处理,那么面试官就会觉得我们有防御性编程的习惯,能够写出鲁棒性的软件。

当然并不是所有与鲁棒性相关的问题都只是检查输入的参数这么简单。我们看到问题的时候,要多问几个“如果不。。。那么。。。”这样的问题。比如面试图15“链表中倒数第K个结点”,这里隐含一个条件就是链表中结点的个数大于k。我们要问如果链表中的结点的数目不是大于k个,那么,代码会出现什么问题?主要的思考方式能够帮助我们发现潜在的问题并提前解决问题。这比让面试官发现问题之后我们再去慌忙分析代码查找问题的根源要好得多。

面试15:链表中倒数第k个结点

面试16:反转链表

面试17:合并两个排序的链表

输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然是按照递增排序的。

关于此问题,应聘者容易犯两种错误:
(1)在写代码之前并没有对合并的过程想清楚,最终合并出来的链表要么中间断开了要么并没有做到递增排序;
(2)代码在鲁棒性方面存在问题,程序一旦有特殊的输入(如空链表)就会崩溃。

此题考点
(1)应聘者分析问题的能力。解决这个问题需要大量的指针操作,应聘者如果没有透彻地分析问题形成清晰的思路,那么他很难写出正确的代码
(2)能不能写出鲁棒性的代码。由于有大量指针操作,应聘者如果稍有不慎就会在代码中遗留很多与鲁棒性相关的隐患。建议应聘者在写代码之前全面分析哪些情况会引入空指针,并考虑清楚怎么处理这些空指针。

关于高质量的代码 小结

主要从规范性、完整性和鲁棒性3个方面介绍了如何在面试时写出高质量的代码

规范性:书写清晰、布局清晰、命名合理
完整性:完成基本功能、考虑边界条件、做好错误处理
鲁棒性:采取防御式编程、处理无效的输入

大多数的面试都是要求应聘者在白纸或者白板上写代码。应聘者在编码的时候要注意规范性,尽量清晰地书写每个字母,通过缩进和对齐括号让代码布局合理,同时合理命名代码中的变量和函数

最好在编码之前全面考虑所有可能的输入,确保写出的代码在完成了基本功能之外,还考虑了边界条件,并做好了错误处理。只有全面考虑到这3方面的代码才是完整的代码

另外要确保自己写出的程序不会轻易崩溃。平时在写代码的时候,应聘者最好养成防御式编程的习惯,在函数入口判断输入是否有效并对各种无效输入做好相应的处理。

你可能感兴趣的:(C++,Data,structure,&,algorithm,notes,剑指offer)