Clean Code 之旅 - 类与迭进

462 阅读5分钟

在之前的章节中,介绍了如何写好一个函数。关键点就是设计出来的函数要足够小,保证一个函数只做一件事情,从而我们可以直接通过函数的名字来确定函数具体做了什么事情,甚至都不用读具体的代码,我们是通过层层封装来做到这一点的。

那么这次再来讨论一个比函数稍微高层一点的结构 - 类,我们该如何设计好一个类呢?这里的关键点还是小,但是与函数不同的是,这次我们不能仅仅从代码的行数去判断一个类是否足够小,我们需要从类的职责来做判断。在面向对象设计中,有一个很重要的原则就是 单一职责原则,作者在这里用了一个很好的比喻,我们可以把函数或是变量看作是一个个的工具,然后把类看作是存储这些工具的抽屉。你可以选择把所有的工具都放在一个抽屉里,你也可以选择一个拥有很多抽屉的工具箱,然后把这些工具分门别类地放好,前者你几乎不需要过多的设计,甚至是思考,抽屉形同虚设,你只是需要一个放工具的地方罢了,而后者则不同,它可以帮你把工具分类,存取的时候都很方便。

书中还谈到了一个 粘合度 的概念,说的是类中的方法和类的关系,一个方法用到的类中的属性越多,说明这个方法和类的 粘合度 越高。方法和类的粘合度越高,就会和类形成一个逻辑整体,如果说一个方法和类没有任何的 粘合度,那么其实这个方法是可以从该类中拆分出来的,你可以借助这一点把一个类合理地通过拆分,让其变小。

最后还提到一点就是,我们希望设计出来的系统,在面对增加新的功能的时候,能够直接增加代码而不去修改之前的代码。这一点之所以很重要是因为修改比新增更容易出错,你需要保证修改后不影响之前的逻辑,而且还需要重新修改测试,如果说之前的代码的作者并不是你本人,你还要试着去理解别人的思路,理解别人写的每一句代码,不然的话你不敢保证经过你改写的东西对之前的功能不会造成任何的影响。我们完全可以利用面向对象设计中的一些原则和知识来做到在保证不修改之前的代码的情况下增添新的功能,比如 DIP 原则说的就是我们应该依赖于抽象,而不是具体的类。依赖于抽象,然后通过父类类型表示子类类型,再加上多态的实现,我们就可以避免去使用 if - else 这种需要修改代码的逻辑。


迭进

老实说,当看到 emergence 这个词,我其实并不知道它具体的意思,后来看了翻译才知道。如果说一个项目要不断地进行更进,这里我们需要注意什么呢?作者在这里给出了 4 个建议,并且这 4 个建议都是按重要性从大到小排好序的:

  1. 通过所有的测试

    如果一个系统不能够被验证,那么说明这个系统还不能够被部署上线,这是因为你并不清楚这里面是否存在问题,特别是当一个系统大到无法通过表面的测试面面俱到的时候。而且写测试还能够帮助设计,像是利用 TDD 这样的方式,我们至少能够保证写出来的东西是可测的,而且还可以有效地对模块进行拆解。

  2. 不含有重复的代码

    这个说实在其实挺难的,有些时候两个业务模块做的其实是同样的事情,但是因为参数条件什么的不一样,我们就会写出两个逻辑相同的代码块。减少重复的代码块可以有效减少阅读和维护代码的成本,但是个人认为这并不是一次就可以做到的,你需要耐心地反复地看自己之前写的代码,不断地去审视代码,看有没有能够改进的地方,然后一点点做优化,明白如何重用小的模块比明白重用大的模块更重要,有了这些认识,再加上耐心地尝试,你的代码的质量会慢慢地变好,切记不能急于求成。

  3. 代码的表达要清晰

    之前我们说到的命名,拆解大的模块的目的就是为了让每一个函数都能够清晰地展现它自己的功能。当一个系统变得越来越大,那么开发人员就会花越多的时间去阅读代码,软件工程的主要开销是在长期的维护上,但没有什么比 “关心” 更能够减小开销了,时常检查你的代码,思考此时的代码能否被别人理解,换一种表达方式能不能变得更好等等,当你在代码中充入了足够的心血,那么就会得到好的结果。

  4. 使用更少的类和函数

    这一点主要是为了让整个系统尽可能的小,当然不要忘了这是 4 条当中重要性最低的一个。


总结

书中描述的这些简单的原则都是作者十几年甚至是几十年总结下来的东西,通过不断犯错,不断尝试,不断积累才有了书中的精华。我们通过几天,甚至几小时的阅读就可以认识到这些东西,何尝不是极其幸运。我突然记起自己之前问自己的一个问题 “写代码是否有捷径可走?”,但现在看来,很多时候捷径就在眼前,但自己走都不想走。这又要怪谁呢?