编写可读代码的艺术:末·代码块&测试

2017-04-21  本文已影响129人  sunnyaxin

前言:上篇记录了如何改变程序的“循环与逻辑”来让代码更有可读性,包括几种技巧,这些技巧都需要对代码结构做出微小的改工,具体见这里。本文在前两篇的基础上,讨论第三部分,在函数级别对代码做更大的改动,以及第四部分,可读测试代码的重要性。


*第一部分:重新组织代码 *

如何编写可读代码

  1. 简化命名、注释和格式的方法,使每行代码都言简意赅。
  2. 梳理程序中的循环、逻辑和变量来减小复杂度并理清思路。
  3. 在函数级别解决问题,例如重新组织代码块,使其一次只做一件事。
  4. 编写有效的测试代码,使其全面简洁,同时可读性更高。

抽取不相关的子问题

在工程学中,主要研究的问题是关于把大问题拆成小问题,再把这些问题的解决方案放回一起。带代码中也类似,主要考虑的问题如下:
(1)针对某个函数或代码块,思考这段代码的高层次目标是什么?
(2)针对每一行代码,思考其是否为了直接目标而工作,其高层次目标是什么?
(3)若由足够的行数在解决不相关的子问题,抽取代码到独立的函数中。

也就是,把一般代码和项目专有的代码分开。使得最后大部分都是一般代码,通过建立一大组库和辅助函数来解决一般问题,剩下的只是让程序与众不同的核心部分。

纯工具代码

一些核心任务,大多数程序都会做,例如操作字符串、使用哈希表以及读/写文件。通常,这些“基本工具”是由编程语言中内置的库来实现的,对于没有的方法,需要自己来完成空白,也就是一个不相关的子问题,并应抽取到一个新的函数中。比如:读取文件函数:ReadFileToString()。

创建大量通用代码

在项目中广泛适用,常常有个专门的目录来存放这种代码(例如util),方便重用。通用代码有很多好处,完全从项目的其他部分中解耦出来,容易开发,容易测试,并且容易理解。

简化已有接口

创建自己的包装函数,隐藏不理想的接口。

过犹不及

为代码增加一个函数存在一个小的(却有形的)可读性代价,要注意适度,根据项目需求,付出代价满足得到成效才可以。

一次只做一件事

  1. 应该把代码组织得一次只做一件事情,如果所有代码纠缠在一起,对于每个任务都很难靠其自身来理解它从哪里开始,到哪里结束。
  2. 如果代码很长很难理解,尝试把其所做的所有任务列出来,其中一些任务可以很容易地变成单独的函数(或类)。其他的可以简单地成为一个函数中的逻辑“段落”。
  3. 难点:准确描述程序所做的小事情。

把想法编变成代码

简单技巧:用自然语言描述程序然后用这个描述来写出更自然的代码。所做的任务如下:

(1)像对着一个同事一样用自然语言描述代码要做什么    
(2)注意描述中所用    
(3)写出与描述所匹配的代码

具体的需要做到如下几点:
1、清楚地描述逻辑
2、了解函数库是有帮助的
3、把这个方法应用于更大的问题
(1)用自然语言描述解决方案
(2)递归使用(1)

少写代码

所写的每一行代码都是要测试和维护的,通过重用库或者减少功能,可以节省时间并且使代码库保持精简节约。可以通过以下几种方法少写代码:

  1. 保持小代码库
    (1)创建越多越好的“工具”代码来减少重复代码
    (2)减少无用代码或没有用的功能
    (3)项目保持分开的子项目状态
    (4)保持代码又轻又灵
  2. 删除独立的函数很简单,不让无用代码交织在项目中。
  3. 熟悉周边的库:每隔一段时间,花15分钟来阅读标准库中的所有函数/模块/类型的名字。
  4. 重用库有着很多好处,不仅节省时间,而且少写代码。在一个成熟的库中,每一行代码都代表相当大量的设计、调试、重写、文档、优化和测试。任何经受了这样达尔文进化过程一样的代码行都是很有价值的。

第二部分 测试与可读性

测试:任何仅以检查另一段代码的行为为目的的代码。

  1. 使测试易于阅读和维护。
    测试代码的可读性和非测试代码是同样重要的,可以把测试代码看做非正式的文档,它记录了真实代码是如何工作和应该如何使用。测试应当具有可读性,以便其他程序员可以舒服地改变或者增加测试。

  2. 测试原则:对使用者隐去不重要的细节,以便更重要的细节会更突出。

  3. 测试基本内容:对于这样的输入/情形,期望有这样的行为/输出。
    这个目的很多时候可以用一行代码来表达,使代码紧凑而又易读,让测试的表述保持很短还会让增加测试变得很简单。

  4. 让错误消息具有可读性:如果测试失败了,所发出的错误消息应该能容易跟踪并修正这个bug。

  5. 测试输入:选择一组最简单的输入,能完整的使用被测代码。

  6. TDD测试驱动开发
    测试驱动开发是一种编程风格,在写真实代码之前就写出测试。一般来讲,如果在设计代码时发现,这对测试来说是噩梦,那么就应该重新考虑这个设计。

如表所示为可测性差的代码的特征,以及它所带来的设计问题:

特征 可测性的问题 设计问题
使用全局变量 对于每个测试都要重置所有的全局状态(否则,不同的测试之间会相互影响) 很难理解哪些函数有什么副作用。没办法独立考虑每个函数,要考虑整个程序才能理解是不是所有的代码都能工作
对外部组件有大量依赖的代码 很难给它写出任何测试,因为要先搭起太多的脚手架。写测试会比较无趣,因此人们避免写测试。 系统会更可能因某一依赖失败而失败。对于改动来讲很难知道会产生什么样的影响。很难重构类,并且要考虑更多恢复路劲
代码又不确定的行为 测试会很古怪,而且不可靠。经常失败的测试最终会被忽略。 这种程序更可能会有条件竞争或者其他难以重现的bug。这种程序很难推理。产品中的bug很难跟踪和改正。

另一方面,如果设计的代码容易写出测试,是个好现象。如下表所示为可测性较好的代码的特征,以及它所产生的优秀设计:

特征 对可测性的好处 对设计的好处
类中只有很少或者没有内部状态 很容易写出测试,因为测试一个方法只要较少的设置,并且有较少的隐藏状态需要检查 有较少状态的类更简单,更容易理解
类/函数只做一件事 要测试它只需要较少的测试用例 较小/较简单的组件更加模块化,并且一般来讲系统有更少的耦合
每个类对别的类的依赖很少;低耦合 每个类可以独立地测试(比多个类一起测试容易的多) 系统可以并行开发。可以很容易修改或者删除类,而不会影响系统的其他部分
函数的接口简单,定义明确 有明确的行为可以测试。测试简单接口所需的工作量较少 接口更容易让程序员学习,并且重用的可能性更大

小结

《编写可读代码的艺术》这本书看完了,也总结了三篇读书笔记,分别为编写可读代码的艺术:初·代码审美编写可读代码的艺术:次·循环逻辑优化编写可读代码的艺术:末·代码块&测试。 这三篇文章中,对代码的优化的范围越来越大,难度也越来越高,从基本的变量名字修改到循环逻辑优化,再到本文中整个代码块的设计和测试代码的可读性,到这里时,已经不是寥寥数语可以记录的,知识点和理论很少,说起来也很简单,我甚至把四大部分中的后两部分合并写做一篇博客,因为讲起来真的很容易,但需要的是大量的实践和总结。原来觉得一个高级程序员或者说有经验的程序员,就是代码量很多,做过很多项目,现在才觉得,代码的设计,项目的结构,合理的测试等等,这些才是更重要的。写代码这件事情,并不是表面看起来那么的体力活,或许每个程序员都应该把自己看做一个作家,虽然这个世界上有很多种语言,我们的目的不是熟练某一种语言,也不是仅仅能够写出完整的“作品”就可以了,更重要的,是写出“艺术感”的“作品”。知识的海洋太过浩渺,世界上的聪明人数不甚数,撸起袖子加油干,共勉。

上一篇下一篇

猜你喜欢

热点阅读