外观
01.Clean Code
约 5408 字大约 18 分钟
项目管理数据结构算法devops
2022-08-19
第1章 整洁代码
1.1 要有代码
可以创造帮助把需求解析和汇整为正式结构的各种工具。然而,永远无法抛弃必要的精确性——所以代码永存。
1.2 糟糕的代码
有朝一日再回头清理,勒布朗法则:稍后等于永不。
1.3 混乱的代价
对代码的每次修改都影响到其他两三处代码。
1.3.1 华丽新设计
新团队必须搭建一套新系统,要能实现旧系统的所有功能。另外,还得跟上对旧系统的持续改动。在新系统功能足以抗衡旧系统之前,管理层不会替换掉旧系统。竞赛可能会持续极长时间。曾见过延续了十年之久的,最后新系统老了。
1.3.2 态度
① 多数经理想要好代码,即便他们总是痴缠于进度。
② 程序员遵从不了解混乱风险的经理的意愿,也是不专业的做法。
1.3.3 谜题
制造混乱无助于赶上期限。
1.3.4 整洁代码的艺术
① 写整洁代码,需要遵循大量的小技巧,贯彻刻苦习得的“整洁感”。
② 有“代码感”的程序员能从混乱中看出其他的可能与变化。“代码感”帮助程序员选出最好的方案,并指导程序员制订修改行动计划,按图索骥。
1.3.5 什么是整洁代码
① 糟糕的代码引发混乱!别人修改糟糕的代码时,往往会越改越烂。
② 破窗理论。
③ 整洁的代码力求集中,每个函数、每个类和每个模块都全神贯注于一事,完全不受四周细节的干扰和污染。
④ 单元测试和验收测试。
⑤ 能通过所有测试。
⑥ 体现系统中的全部设计理念。
⑦ 包括尽量少的实体,比如类、方法、函数等。
⑧ 没有重复代码。
⑨ 消除重复和提高表达力对整理代码有益。
1.4 思想流派
1.5 我们是作者
这事概无例外。不读周边代码的话就没法写代码。编写代码的难度,取决于读周边代码的难度。要想干得快,要想早点做完,要想轻松写代码,先让代码易读。
1.6 童子军军规
如果每次签入时,代码都比签出时干净,那么代码就不会腐坏。
1.7 前传与原则
在本书中,你会发现对不同设计原则的引用,包括单一权责原则(SSRP)、开放闭合原则(OCP)和依赖倒置原则(DIP)等。
1.8 小结
只是展示好程序员的思维过程,还有使用的技巧、技术和工具。
第2章 有意义的命名
2.1 介绍
软件中随处可见命名。
- 变量、函数、参数、类和封包命名。
② 源代码及源代码所在目录命名。我们给jar文件、war文件和ear文件命名。
2.2 名副其实
魔术数字的坏处。
数值的意义难以了解。
数值需要变动时,可能要改不只一个地方。
2.3 避免误导
① 程序员必须避免留下掩藏代码本意的错误线索。避免使用与本意相悖的词。例如,UNIX平台或类UNIX平台的专有名称。
- 提防使用外形相似度较高的名称。
③ 以同样的方式拼写出同样的概念才是信息。禁止使用小写字母l和大写字母O作为变量名。
2.4 做有意义的区分
① 如果程序员只是为满足编译器或解释器的需要而写代码,就会制造麻烦。
例如,因为同一作用范围内两样不同的东西不能重名,你可能会随手改掉其中一个名称。
- 注意,只要体现出有意义的区分。
Info和 Data就像a、an和 the一样,是意义含混的废话。
③ 废话都是冗余。
Variable一词永远不应当出现在变量名中。Table一词永远不应当出现在表名中。
2.5 使用读得出来的名称
人类长于记忆和使用单词。大脑的相当一部分就是用来容纳和处理单词的。单词能读得出来。人类进化到大脑中有那么大的一块地方用来处理言语。
在给新开发者解释变量名的意义时,他们总是读出傻乎乎的自造词,而非恰当的英语词。
2.6 使用可搜索的名称
① 便于搜索的好变量名。
② 长名称胜于短名称,搜得到的名称胜于用自造编码代写旧的名称。
单字母名称仅用于短方法中的本地变量。名称长短应与其作用域大小相对应。
若变量或常量可能在代码中多处使用,则应赋其以便于搜索的名称。
2.7 避免使用编码
2.7.1 匈牙利语标记法
2.7.2 成员前缀
2.7.3 接口与实现
2.8 避免思维映射
专业程序员了解、明确是王道。专业程序员善用其能,编写其他人能理解的代码。
2.9 类名
类名和对象名应该是名词或名词短语,类名不应当是动词。
2.10 方法名
方法名应当是动词或动词短语。属性访问器、修改器和断言应该根据其值命名,并依Javabean标准1加上 get、set和 is前缀。
2.11 别抖机灵
名称别耍宝。
2.12 每个概念对应一个词
给每个抽象概念选一个词,并且一以贯之。
反例:controller、manager、driver,感觉没什么区别。
2.13 别用双关语
避免将同一单词用于不同目的。
2.14 使用解决方案领域名称
尽管用那些计算机科学(Computer Science)术语、算法名、模式名、数学术语。
2.15 使用源自所涉问题领域的名称
负责维护代码的程序员就能去请教领域专家。
2.16 添加有意义的语境
用有良好命名的类、函数或名称空间来放置名称,给读者提供语境。给名称添加前缀。
2.17 不要添加没用的语境
只要短名称足够清楚,就要比长名称好。别给名称添加不必要的语境。
2.18 最后的话
取好名字最难的地方在于需要良好的描述技巧和共有文化背景。
第3章 函数
3.1 短小
函数的第一规则是要短小。200行以内,每行80个字符。
if、for、while语句单独占一行,嵌套<3级
3.2 只做一件事
函数应该做一件事。做好这件事。只做这一件事。
判断函数是否不止做了一件事,看是否能再拆出一个函数,该函数不仅只是单纯地重新诠释其实现。
3.3 每个函数一个抽象层级
① 函数中的语句都要在同一抽象层级上。
② 读者可能无法判断某个表达式是基础概念还是细节。
代码分层:低抽象、中抽象、高抽象。
自顶向下读代码:向下规则
每个函数后面都跟着位于下一抽象层级的函数,就能循抽象层级向下阅读了。这叫做向下规则。
保持函数短小、确保只做一件事的要诀。让代码读起来像是一系列自顶向下的TO起头段落是保持抽象层级协调一致的有效技巧。
3.4 switch 语句
单一权责原则(Single Responsibility Principle2, SRP)
开放闭合原则(Open Closed Principle3, OCP),拒绝每当添加新类型时,就必须修改之。
3.5 使用具有描述性的名称
长而具有描述性的名称,要比短而令人费解的名称好。长而具有描述性的名称,要比描述性的长注释好。
选择描述性的名称能理清你关于模块的设计思路,并帮你改进之。
命名方式要保持一致。使用与模块名一脉相承的短语、名词和动词给函数命名。
3.6 函数参数
输出参数比输入参数还要难以理解。
一般必须<3个。
3.6.1 单参数函数的普遍形式
尽量编写遵循这些形式的单参数函数,使用输出参数而非返回值令人迷惑。转换结果就该体现为返回值。
3.6.2 标识参数
标识参数丑陋不堪。向函数传入布尔值简直就是骇人听闻的做法。大声宣布本函数不止做一件事。
3.6.3 双参数函数
尽量利用一些机制(eg:结构体)将其转换成单参数函数。
3.6.4 三参数函数
有三个参数的函数要比双参数函数难懂得多。排序、琢磨、忽略的问题都会加倍体现。
3.6.5 参数对象
如果函数看来需要两个、三个或三个以上参数,就说明其中一些参数应该封装为类了。
3.6.6 参数列表
向函数传入数量可变的参数;
有可变参数的函数可能是一元、二元甚至三元。超过这个数量就可能要犯错了。
3.6.7 动词与关键字
给函数取个好名字,能较好地解释函数的意图,以及参数的顺序和意图。
对于一元函数,函数和参数应当形成一种非常良好的动词/名词对形式。
3.7 无副作用
函数承诺只做一件事,但还是会做其他被藏起来的事。
无论哪种情况,都是具有破坏性的,会导致古怪的时序性耦合及顺序依赖。
输出参数
参数多数会被自然而然地看作是函数的输入项。
应避免使用输出参数。如果函数必须要修改某种状态,就修改所属对象的状态。
3.8 分隔指令与询问
函数应该修改某对象的状态,或是返回该对象的有关信息。
3.9 使用异常替代返回错误码
3.9.1 抽离try/catch代码块
3.9.2 错误处理就是一件事
函数应该只做一件事。错误处理就是一件事。
3.9.3 Error.java依赖磁铁
3.10 别重复自己
重复可能是软件中一切邪恶的根源。许多原则与实践规则都是为控制与消除重复而创建。
3.11 结构化编程
结构化编程的目标和规范,但对于小函数,这些规则助益不大。
只要函数保持短小,偶尔出现的return、break或continue语句没有坏处,甚至还比单入单出原则更具有表达力。
另外一方面,goto只在大函数中才有道理,所以应该尽量避免使用。
3.12 如何写出这样的函数
配上一套单元测试,覆盖每行丑陋的代码。
打磨这些代码,分解函数、修改名称、消除重复。缩短和重新安置方法。有时还拆散类。同时保持测试通过。
3.13 小结
函数是语言的动词,类是名词。
第4章 注释
注释总是一种失败。无法找到不用注释就能表达自我的方法,所以总要有注释,这并不值得庆贺。
程序员不能坚持维护注释。
程序员应当负责将注释保持在可维护、有关联、精确的高度。
主张把力气用在写清楚代码上,直接保证无须编写注释。
不准确的注释要比没注释坏得多。
4.1 注释不能美化糟糕的代码
带有少量注释的整洁而有表达力的代码,要比带有大量注释的零碎而复杂的代码像样得多。
4.2 用代码来阐述
代码本身不足以解释其行为。
4.3 好注释
4.3.1 法律信息
公司代码规范要求编写与法律有关的注释。在每个源文件开头注释处放置的内容。
4.3.2 提供信息的注释
用注释来提供基本信息也有其用处。
4.3.3 对意图的解释
提供了某个决定后面的意图。
4.3.4 阐述
注释把某些晦涩难明的参数或返回值的意义翻译为某种可读形式。
4.3.5 警示
用于警告其他程序员会出现某种后果的注释也是有用的。
4.3.6 TODO注释
有理由用//TODO形式在源代码中放置要做的工作列表。
4.3.7 放大
注释可以用来放大某种看来不合理之物的重要性。
4.3.8 公共API中的javadoc
4.4 坏注释
4.4.1 喃喃自语
不要写只有自己能懂的注释。
4.4.2 多余的注释
不能比代码本身提供更多的信息。没有证明代码的意义,没有给出代码的意图或逻辑。读它并不比读代码更容易。事实上,它不如代码精确。
4.4.3 误导性注释
4.4.4 循规式注释
所谓每个函数都要有Javadoc 或每个变量都要有注释的规矩全然是愚蠢可笑的。
4.4.5 日志式注释
有人会在每次编辑代码时,在模块开始处添加一条注释。
在模块开始处创建并维护这些记录还算有道理。
冗长的记录只会让模块变得凌乱不堪,应当全部删除。
4.4.6 废话注释
程序员应该意识到,他的挫败感可以由改进代码结构而消除。
4.4.7 可怕的废话
4.4.8 能用函数或变量时就别用注释
4.4.9 位置标记
程序员喜欢在源代码中标记某个特别位置。
尽量少用标记栏,只在特别有价值的时候用。
4.4.10 括号后面的注释
程序员会在括号后面放置特殊的注释。
4.4.11 归属与署名
源代码控制系统是这类信息最好的归属地。
4.4.12 注释掉的代码
已经拥有优良的源代码控制系统如此之久,这些系统可以为我们记住不要的代码。我们无需再用注释来标记,删掉即可,它们丢不了。
4.4.13 HTML注释
4.4.14 非本地信息
确保它描述了离它最近的代码。
4.4.15 信息过多
别在注释中添加有趣的历史性话题或者无关的细节描述。
4.4.16 不明显的联系
注释及其描述的代码之间的联系应该显而易见。
4.4.17 函数头
短函数不需要太多描述。
4.4.18 非公共代码中的Javadoc
4.4.19 范例
第5章 格式
5.1 格式的目的
代码的可读性会对以后可能发生的修改行为产生深远影响。
原始代码修改之后很久,其代码风格和可读性仍会影响到可维护性和扩展性。即便代码已不复存在,你的风格和律条仍存活下来。
5.2 垂直格式
多数都小于200行。
5.2.1 向报纸学习
源文件最顶部应该给出高层次概念和算法。细节应该往下渐次展开,直至找到源文件中最底层的函数和细节。
5.2.2 概念建垂直方向上的区隔
每行展现一个表达式或一个子句,每组代码行展示一条完整的思路。这些思路用空白行区隔开来。
5.2.3 垂直方向上的靠近
靠近的代码行则暗示了它们之间的紧密关系。
5.2.4 垂直距离
关系密切的概念应该互相靠近。(注意:不适用于分布在不同文件中的概念)
变量声明。变量声明应尽可能靠近其使用位置。
实体变量。应该在类的顶部声明。
相关函数。若某个函数调用了另外一个,就应该把它们放到一起,而且调用者应该尽可能放在被调用者上面。能轻易找到被调用的函数,极大地增强了整个模块的可读性。
概念相关。概念相关的代码应该放到一起。相关性越强,彼此之间的距离就该越短。
5.2.5 垂直顺序
自上向下展示函数调用依赖顺序。建立了一种自顶向下贯穿源代码模块的良好信息流。
5.3 横向格式
每行<80个字符。
5.3.1 水平方向上的区隔与靠近
乘法因子之间没加空格,因为具有较高优先级。加减法运算项之间用空格隔开,因为加法和减法优先级较低。
多数代码格式化工具都会漠视运算符优先级,从头到尾采用同样的空格方式。
5.3.2 水平对齐
使用水平对齐来强调某些程序结构。
尽力对齐一组声明中的变量名,或一组赋值语句中的右值。我
5.3.3 缩进
5.3.4 空范围
5.4 团队规则
团队认同同一种风格。
好的软件系统是由一系列读起来不错的代码文件组成的。它们需要拥有一致和顺畅的风格。读者要能确信,他们在一个源文件中看到的格式风格在其他文件中也是同样的用法。
5.5 “鲍勃大叔”的格式规则
第6章 对象和数据结构
将变量设置为私有(private): 不想其他人依赖这些变量。能自由修改其类型或实现。
6.1 数据抽象
不愿曝露数据细节,更愿意以抽象形态表述数据。这并不只是用接口和/或赋值器、取值器就万事大吉。要以最好的
方式呈现某个对象包含的数据,需要做严肃的思考。傻乐着乱加取值器和赋值器,是最坏的选择。
6.2 数据、对象的反对称性
对象和数据结构之间的二分原理:过程式代码难以添加新数据结构,因为必须修改所有函数。面向对象代码难以添加新函数,因为必须修改所有类。
在任何一个复杂系统中,都会有需要添加新数据类型而不是新函数的时候。这时,对象和面向对象就比较适合。
想要添加新函数而不是数据类型的时候,过程式代码和数据结构更合适。
6.3 德墨忒耳律
模块不应了解它所操作对象的内部情况。
得墨忒耳律认为,类C的方法f只应该调用以下对象的方法:
C
由f创建的对象;
作为参数传递给f的对象;
由C的实体变量持有的对象。
方法不应调用由任何函数返回的对象的方法。换言之,只跟朋友谈话,不与陌生人谈话。
6.4 数据传送对象
6.5 小结
6.6 文献
第7章 错误处理
概要列出编写既整洁又强固的代码——雅致地处理错误代码的一些技巧和思路。
ps:java有自己的异常处理机制
7.1 使用异常而非返回码
7.2 先写Try-Catch-Finally语句
7.3 使用不可控异常
7.4 给出异常发生的环境说明
7.5 依调用者需要定义异常类
7.6 定义常规流程
7.7 别返回NULL值
7.8 别传递NULL值
7.9 小结
7.10 文献
第8章 边界
购买第三方程序包、使用开放源代码、依靠公司中其他团队打造组件或子系统。都得将外来代码干净利落地整合进自己的代码中。将介绍一些保持软件边界整洁的实践手段和技巧。
8.1 使用第三方代码
在接口提供者和使用者之间,存在与生俱来的张力。第三方程序包和框架提供者追求普适性,这样就能在多个环境中工作,吸引广泛的用户。而使用者则想要集中满足特定需求的接口。这种张力会导致系统边界上出现问题。
可以利用结构体和函数封装,得到易于理解、易用的函数。
8.2 浏览和学习边界
在学习性测试中,就像在应用中那样调试第三方代码,基本上都是通过核对试验来检测自己对那个API的理解程度。测试聚焦于我们想从API得到的东西。

