读完《clean code》一书,有些想法,想要记录下来。
《clean code》在一年前字节实习的时候就听同事推荐过,但一直没有下决心来看。本学期软件建模课程的刘岩老师又一次提起了它,在第一节课上,她提了两本书建议我们看看:《Domain Driven Design》和《clean code》。第一本书是关于领域驱动设计的,我看过后备受启发。这本书打开了我对面向对象的大门,让我发现了我之前写的代码(MVC)不过是一堆贫血模型和CRUD。本文不是谈DDD的,因此对《Domain Driven Design》也就点到为止。对于《clean code》,我在开始看前的刻板印象都是些繁碎教条,就像每个项目都有的《编码风格规范》一样。然而因为第一本书《DDD》的巨大“成功”,我开始尝试看《clean code》。
相较于《Domain Driven Design》从宏观的、架构角度去谈软件设计,《clean code》本书从具体的、代码角度去谈软件开发,目标很明确,就是怎么写出“整洁”的代码。那么,何谓整洁的代码呢?我现在的理解是:权责分明,命名丰富,抽象到位。
关于权责分明,指的就是单一职责的设计原则,即一个类或函数应该只做一件事,只承担一个职责。另一条设计原则是开闭原则,即对修改封闭,对扩展开放。只有当你的类做到很好地划分,权责分明时,你才可能做到对修改封闭,代码修改都将集中在一处。借助设计模式,你可以做到对扩展开放,新功能可以比较容易的添加。
似乎这两个设计原则都很耳熟?似乎都听过但从来都不知道如何实践?是的我也是如此。在读《clean code》之前,我对这些设计原则嗤之以鼻,这就像置在高阁的圣经,都是对的,但有什么用呢?不过我现在开始逐渐理解了,或许是之前的编程的经验不够吧。《clean code》此书中作者给出了他开发遇到的现实的例子,来讲解为什么这样的代码是不好的、为什么修改后的代码是好的、哪些情况表明了职责过多、而怎样才体现了职责单一、哪里体现了开闭原则、又如何把握开闭原则。在这里我并不尝试去讲解这两个设计原则,因为我不认为我短短篇幅能讲得清楚,我建议你去亲自读读这本书。
关于命名丰富,指的是对编码中的各类命名都要精心思考,每一次命名都是我们重新表达自己的机会。一个常见的反例是变量命名为a,b,c这种单字母,让读者不知所云。写代码和写散文没什么区别,你应该考虑读者的感受,更不用说下一次读这个代码的人很可能就是你自己。一个好的命名,可以让你代码读起来像一篇散文,函数名就像一个段落,说着一个新话题。我们需要珍惜每一次的命名,因为每一次命名都是我们重新表达自己的机会。原来的我,不知道如何去取个好名字,由于英语水平有限,我往往需要取很长的名字才能表达我的意思,我会怀疑取太长的名字不太好。而这本书给出了答案,短的但含义模糊的命名远比长的但含义明确的命名糟糕。
关于抽象到位,我印象最深刻的点是“在同一抽象层次编码”。听到抽象,大家的印象往往是大层次上的抽象,比如架构层次抽象,设计模式抽象,类继承抽象这些概念。但抽象其实还能细致到函数、代码中间。函数间的抽象指的是,函数关注的点应该是单一的。一个函数不应该又负责底层细节操作,又调用同层其他函数。常见的情形就是胶水代码,A函数中要调用B函数,但A函数的输入参数不符合B函数的输入参数,因此A函数需要先对输入参数做转换,然后调用B函数,中间这样的转换就是胶水代码。如果这样的转换很简单,并且函数逻辑并不复杂,这样的代码也还能接受。如果胶水代码本身就很复杂了,读者就会淹没在转换的细节中,而没法理解函数真正的意图。这时候正确的做法是,将胶水代码抽离出去,作为一个单独函数。更进一步的是,将胶水代码涉及到数据结构抽象为一个单独的类,专门负责数据转换。你看,这里也有单一职责原则的体现哦。
读完《clean code》,就像睁开了一只眼。有很多新想法就不细写了,包括单元测试是多么美妙,注释是一剂苦药。书籍的含金量远高于单独的帖子,很多网上的帖子都是拾人牙慧,倒不如追根溯源,去看看源头的书。系统性的书籍可比一个个单独的帖子好太多了。此外《clean code》的中文翻译得很不错,感谢翻译的付出。