最近因为一些契机,花了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原则)
到这为止把,这里只是列出比较基本的,详情可以阅读书籍。
纸上得来终觉浅,绝知此事要躬行。 ——出自《冬夜读书示子聿》
「真诚赞赏,手留余香」
请我喝杯咖啡?
使用微信扫描二维码完成支付
