代码整洁之道

标签: book   clean code   java  

最近因为一些契机,花了2到3天看完**《代码简洁之道》**这本书,英文名为:Clean Code

开篇就有有这么简单一句话:“阅读这本书有两种原因:第一,你是个程序员;第二,你想成为更好的程序员”。是的,没错,我想成为更好的程序员。所以我决定好好阅读下这本书。

回想2019年,确实是我最忙碌的一年,做过几个项目,其中部分涉及到架构,但是大部分时间都关注在如何实现业务代码,没有更好的关注到代码本身的细节。虽然经常了解到关于整洁代码相关的知识,在工作中也或多或少的用到,比如每天的code review以及TDD开发等等。但是确实不够系统,大多时间忙于完成业务,通过阅读本书,以及其中大量简洁代码的指南,受益匪浅。

简洁代码

那么到底“什么才是简洁的代码?” “我怎么才能写出简洁的代码呢?”

什么才是简洁的代码?

有多少程序员,就有多少定义。

C++之父- Bjarne Stroustrup

我喜欢优雅和高效的代码。代码逻辑应当直截了当,交缺陷难以隐藏;尽量较少依赖关系,使之便于维护;一句某种分层战略完善错误处理代码;性能调至最优,省得引诱别人做没规矩的优化,搞出一堆混乱来。整洁的代码只做好一件事

软件设计的许多原则最终都会归结为最后这句警语。

Grady Booch, Object Oriented Analysis and Design with Applications:

简洁的代码简单直接,整洁的代码如同优美的散文。整洁的代码从不隐藏设计者的意图,充满了干净利落的抽象和直接了当的控制语句。

Eclipse战略教父 - Dave Thomas

整洁的代码应可由作者之外的开发者阅读和增补。它应当有单元测试和验收测试。它使用有意义的命名。它只提供一种而非多种做一件事的途径。它只有尽量少的依赖关系,而且要明确地定义和提供清晰、尽量少的API。代码应通过其字面表达含义,因为不同的语言导致并非所有必须信息均可通过代码自身清晰表达。

Michael Feathers, Working Effectively with Legacy Code:

我可以列出我留意到的整洁代码的所有特点,但其中有一条根本性的。整洁的代码总是看起来像是某位特别在意它的人写的。几乎没有改进的余地。代码作者什么都想到了,如果你企图改进它,总会回到原点,赞叹某人留给你的代码–全心投入的某人留下的代码。

Ron Jeffries, Extreme Programming installed:

简单的代码,依其重要顺序:

  • 能通过所有测试
  • 没有重复代码
  • 体现系统中的全部设计理念
  • 包括尽量少的实体,比如类、方法、函数等。

减少重复代码,提高表达力,提早构建简单抽象,这就是我写简洁代码的方法。

不要重复代码,只做一件事,表达力,小规模抽象。

如何写出简洁的代码

有意义的命名

  • 不要名副其实:例如参数名称太模糊
  • 避免误导:例如变量使用UNIX平台的专有名词
  • 做有意义的区分:例如参数名称不要使用a1, … , aN
  • 使用读得出来的名称
  • 使用可搜索的名称:例如:单字母名称和数字常量就很难找出来
  • 避免使用编码:例如一些没有必要的成员变量前缀
  • 避免思维映射
  • 类名:应该是名词或名词短语
  • 方法名:应当是动词或动词短语
  • 别扮可爱:使用一些与文化紧密相关的俚语等
  • 每个概念对应一个词:比如都用controllers结尾等
  • 别用双关语:比如在好多类中都有add方法,但是表达的含义却不一定相同
  • 使用解决方案领域名称
  • 添加有意义的语境,不要添加没用的语境

函数

  • 短小
  • 只做一件事
  • 每个函数一个抽象层级:自顶向下读代码:向下规则
  • Switch语句:确保每个switch都隐藏在较低的抽象层级,而且不重复。可用多态替换
  • 使用描述性的名称
  • 函数参数个数:理想的参数数量是零,其次是一,再次是二,应避免三。有足够特殊的理由才能用三个以上参数
  • 一元函数:避免对函数入参进行转换,但是返回值却为void,将转换结果体现为返回参数要更好
  • 标志参数:避免在函数参数中传递布尔值
  • 二元函数:应尽量利用一些机制将其转成一元函数
  • 三元函数:建议写三元函数前一定要想清楚
  • 参数对象:有两个,三个或者三个以上的参数,可以考虑将其中一些参数封装为类
  • 参数列表
  • 动词与关键字
  • 无副作用
  • 输出参数
  • 分隔指令和询问
  • 使用异常代替返回错误码
  • 抽离Try/Catch代码块
  • 别重复自己

注释

如果代码有足够的表达力,就不那么需要注释—或许根本不需要。

  • 注释不能美化糟糕的代码
  • 用代码来阐述
  • 好的注释:法律信息,提供信息的注释,对意图的解释,阐释,警示,TODO注释,放大,公共API中的Javadoc
  • 坏注释:喃喃自语,多余的注释,误导性注释,循规式注释,日志式注释,废话注释,能用函数名或者变量名就别用注释,位置标记,括号后面的注释,归属和签名,注释掉的代码,HTML注释,非本地信息,信息过多,不明显的联系

格式

保持良好的代码格式,选用一套管理代码格式的简单规则,贯彻它。如果在团队中,那么需要统一采用一套简单的格式规则。

  • 垂直格式:行数不宜过多;概念间垂直方向上的区膈;垂直方向上的靠近;垂直距离(例如相关函数放在一起);垂直顺序(自上而下展示函数调用依赖顺序)
  • 横向格式:代码不要太宽(不要超过120个字);水平方向上的区隔和靠近(如空格的使用,乘法因子之间不需要加空格);水平对齐;缩进;空范围(while或者if语句的语句体为空)

对象和数据结构

对象暴露欣慰,隐藏数据。便于添加新对象类型而无需修改既有行为,同时也难以在既有对象中添加新行为。数据结构暴露数据,没有明显的行为。便于向既有数据结构添加新行为,同时也难以想既有函数添加新数据结构。

  • 数据、对象的反对称性(数据结构暴露其数据,没有提供有意义的函数;对象把数据隐藏于抽象之后,暴露操作数据的函数)
  • 得墨忒耳定律:模块不应了解它所操作对象的内部情形
  • 数据转送对象DTO:精炼的数据结构,是一个只有变量,没有函数的对象
  • Active Record:特殊的DTO,会拥有类似save,find方法,一般都是对数据库表的直接翻译,不应该有业务规则方法

错误处理

简洁的代码是可读的,但也要强固。可读和强固并不冲突。如果将错误处理隔离看待,独立于逻辑之外,就能写出强固而整洁的代码。

  • 使用异常而非返回码
  • 先写Try-Catch-Finally语句
  • 使用不可控异常
  • 给出异常发生的环境说明
  • 依调用者需要定义异常
  • 定义常规流程
  • 别返回null值
  • 别传递null值

边界

将外来代码干净利落地整合进自己的系统,需要保持边界的整洁。

  • 隐藏边界上的接口
  • 将已知和未知分隔开来
  • 边界上的代码需要清晰的分隔和定义了期望的测试

单元测试

TDD三定律:

  • 定律一 在编写不能通过的单元测试前,不可编写生产代码。

  • 定律二 只可编写刚好无法通过的单元测试,不能编译也算不过。

  • 定律三 只可编写刚好足以通过当前失败测试的生产代码。

我们编写测试刚好不通过时,说明我们需要编写生产代码来实现某些功能,这时我们就要着手去编写代码。

整洁测试的要素:可读性,可读性,可读性。重要事说三遍。

每个测试一个断言,每个测试一个概念。

测试的5条(FIRST)准则:

  • 快速(Fast) 测试应该够快快。

  • 独立(Independent) 测试应该相互独立。

  • 可重复(Repeatable) 测试应当在任何环境下重复通过。

  • 自我验证(Self-Validating) 测试应该有布尔值输出。

  • 及时(Timely) 测试应及时编写。

  • 类应该短小(单一权责原则;内聚;保持内聚就会得到许多短小的类)
  • 为了修改而组织(SOLID原则)

到这为止把,这里只是列出比较基本的,详情可以阅读书籍。

纸上得来终觉浅,绝知此事要躬行。 ——出自《冬夜读书示子聿》

「真诚赞赏,手留余香」

请我喝杯咖啡?

使用微信扫描二维码完成支付

相关文章