代码整洁之道笔记

182 阅读12分钟

前言

1、衡量代码的唯一有效标准:WTF/min

2、代码技艺,习艺有两点:知和行。

知:应当习得有关原则、模式和实践的知识;

行:穷尽应知之事,并对其了如指掌,通过刻苦实践掌握它。在实践中,体验自己的失败,观察别人的实践与失败。以人为镜。

第一章 整洁代码

1.1要有代码

1、将需求明确到机器可以执行的细节程度,就是编程要做的事。而这种规约正式代码。

1.2糟糕的代码

1、一个公司的毁灭,赶着推出产品,代码写的乱七八糟,特性越加越多,代码也越来越烂,最后再也没法管理这些代码了。糟糕的代码毁了这家公司。

2、勒布朗法则:稍后等于永不。

1.3混乱的代价

1、混乱的增加->团队生产力持续下降->增加人手->新人不熟悉系统设计->制造更多的混乱

2、混乱的增加,促使开发人员希望重新设计,一部分人参与新系统的开发,一部分人维护老系统,两个系统并行相互竞赛,当新系统赶上老系统时,有可能新系统已经换了几波人了,又是一个怪圈。

3、制造混乱无法帮助我们赶上期限,只能拖慢自己,做的快的唯一方法,就是始终保持代码整洁。

4、优雅和高效的代码,代码逻辑应当直接了当,缺陷难以隐藏;尽量减少依赖关系,使之便于维护;依据某种分层战略完善错误处理代码;性能调至最优,省的引导别人做没规矩的优化。整洁的代码只做好一件事。

5、防止破窗理论。

6、整洁的代码从不隐藏设计者的意图,充满了干净利落的抽象和直截了当的控制语句。

7、整洁的代码应当有单元测试和验收测试;使用有意义的命名;只提供一种做一件事的途径;要明确地定义和提供清晰、尽量少的API;通过字面表达含义。

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

9、编写代码的难度,在于读周边代码的难度,提升效率的一种方式,让代码易读。

第二章 有意义的命名

2.2名副其实

1、命名可以直接理解其含义。

2.3避免误导

1、避免使用某些歧义缩写。

2、指示一组时,尽量少用xxxList。

3、不要使用相似度较大的命名方式。

4、误导性名称,小写l和大写O。

2.4做有意义的区分

1、同一范围内两种东西不能重名,不要使用数字1,2这种形式做区分,意义要明确。

2、xxx,xxxInfo,xxxData毫无区别。

3、冗余的命名,Variable不要出现在变量名中;Table不要出现在表名中;NameString-Name;Customer-CustomerObject。

2.5使用读得出来的名称

1、不要使用自造词,不明白意思的词语很傻逼。

2.6使用可搜索的名称

1、如果变量和常量可能在代码中多次使用,应赋予其便于搜索的名称。(这个主要针对查看代码时搜索的区分度上)

2.7避免使用编码

1、杜绝标记/成员前缀/前导字母。

2.8避免思维映射

2.9类名

1、类名和对象名应该是名词或者名词短语。

2.10方法名

1、方法名应该是动词或者动词短语。

2、重载构造器时,使用描述了参数的静态工厂方法名。(类比于业务异常的静态构造器)

2.11别扮可爱

1、这点也就是别使用无聊的笑话或俗语。

2.12每个概念对应一个词

2.13别用双关语

2.14使用解决方案领域名称

1、技术类的词语都可以。

2.15使用源自所涉问题领域的名称

1、针对问题领域进行命名。

2.16添加有意义的语境

2.17不要添加没用的语境

1、对应应用无需给所有文件都加上项目的指示词。

第三章 函数

3.1短小

1、经验告诉我,函数就该小。

2、代码块和缩进:if,else,while语句等,其中的代码块应该只有一行。

3.2只做一件事

1、函数应该做一件事。做好这件事,只做这一件事。

2、判断一个函数是否只做了一件事,就是看是否能再拆出一个函数,该函数不仅只是单纯地重新实现。

3、只做一件事的函数无法被合理地切分为多个区段。

3.3每个函数一个抽象层级

1、自顶向下读代码:向下规则。

3.4switch语句

1、可以使用多态来拆分switch。

3.5使用描述性的名称

1、长而具有描述性的名称,要比短而令人费解的名称好。

2、别害怕花时间取名字。

3.6函数参数

1、最理想的参数数量是零,其次是一,再次是二,避免三。参数越多越难懂!

2、从测试的角度看,参数越少,越简单,测试用例越少。

3、如果函数需要两个、三个或三个以上参数,就应该封装成类。

4、最好在函数名上能够指示参数的意义。

3.7无副作用

3.8分隔指令与询问

1、函数要么做什么事,要么回答什么事,但二者不可兼得。

3.9使用异常替代返回错误码

1、多个判断出现的时候没使用异常会更简洁。

2、最好将try/catch抽离出来作为一个新的函数,因为它的结构会混淆正常流程。

3、错误处理就是一件事。catch后面不应该有其他的。

3.10别重复自己

3.11结构化编程

1、每个函数中只该有一个return语句,循环中不能有break或continue语句。

2、上面这条不是绝对的。

3.12如何写出这样的函数

1、反复推敲,打磨。

2、函数是语言的动词,类是名词。

第四章注释

注释存在的时间越久,就离其所描述的代码越远。注释并不是越多越好。

4.1注释不能美化糟糕的代码

1、与其对一个代码块进行注释,不如把代码写干净。

4.2用代码来阐述

4.3好注释

1、唯一真正的好注释是你想办法不去写注释。

2、注释把某些晦涩难明的参数或返回值的意义翻译为某种可读形式。

3、警示类的注释是有必要的。

4、todo 工作列表类注释。

5、公共API中的javadoc要写。

4.4坏注释

1、自语类注释。

2、多余的注释。

3、误导性注释。

4、循规式注释。(例如每个方法都要javadoc)

5、日志式注释。

6、废话注释。

7、位置标记。(这个不一定,比如在枚举值比较多的情况下,位置标记还是有必要的)

8、如果想要用注释来标记函数结束符,那就应该简化函数。

9、注释掉的代码,要删除。

10、信息过多的注释。

11、函数头。只为做一件事的函数选个好名字比注释有用。

12、非公共代码中的javadoc

第五章格式

团队中应该要统一一种格式。

5.1格式的目的

1、代码格式是为了更好地沟通,提高代码的可读性。

5.2垂直格式

1、关系密切的概念应该互相靠近。

2、变量声明应该尽可能靠近其使用位置。

3、空行隔离。

4、相关函数应该放在一起,可以一目了然。(在类的实现时,并不是这样)

5.3横向格式

1、应尽量保持代码行短小。

2、空格的使用也是为了分隔概念。

3、水平对齐。

4、缩进。

5.4团队规则

1、一个人有一个人的风格,但在团队中时,团队说了算。

第六章对象和数据结构

6.1数据抽象

6.2数据、对象的反对称性

6.3得墨忒耳律

1、模块不应了解它所操作对象的内部情形。

2、方法不应调用由任何函数返回的对象的方法。只跟朋友谈话,不与陌生人谈话。

3、得墨忒耳律要看是对象还是数据结构。

6.4数据传送对象

1、最为精炼的数据结构,是一个只有公共变量、没有函数的类。DTO

第七章错误处理

7.1使用异常而非返回码

7.2先写tru/catch/finally语句

7.3使用不可控异常

7.4给出异常发生的环境说明

7.5依调用者需要定义异常类

7.6定义常规流程

7.7别返回null值

1、避免NPE的出现。

7.8别传递null值

第八章边界

8.1使用第三方代码

1、第三方程序包和框架提供者追求普适性。

8.2浏览和学习边界

1、不要在生产代码中试验新东西,而是编写测试来浏览和理解第三方代码。

2、学习性测试。

8.3学习log4j

1、这一部分其实讲的是怎么讲一个东西进行封装到自己的类中,而使得这部分与其他应用程序隔离。

8.4学习性测试的好处不只是免费

1、学习性测试确保第三方程序包按照我们想要的方式工作。

8.5使用尚不存在的代码

1、这部分讲的是将一些未知的代码封装起来,可以通过伪造的代码进行测试,这样不需要取太过关系真正封装起来的那些逻辑。

8.6整洁的边界

1、避免我们的代码过多地了解第三方代码中的特定信息。

第九章单元测试

测试驱动开发。

9.1TDD三定律(在编写生产代码前,先编写单元测试)

1、在编写不能通过的单元测试前,不可编写生产代码。

2、只可编写刚好无法通过的单元测试,不能编译也算不通过。

3、只可编写刚好足以通过当前失败测试的生产代码。

9.2保持测试整洁

1、脏测试等同于没测试。

2、测试代码和生产代码一样重要。

3、单元测试让代码可扩展、可维护、可复用。

9.3整洁的测试

1、可读性。

2、测试三个环节:构造-操作-检验。

3、使用面向特定领域的测试语言。

4、测试代码应当简单、精悍、足具表达力,但要和生产代码一般有效。

9.4每个测试一个断言

1、每个测试中的断言数量应该最小化。

2、每个测试一个概念,不要一个测试超长。

9.5F.I.R.S.T

1、快速,测试应当够快。

2、独立,测试之间要相互独立。某个测试不应为下一个测试的设定条件。

3、可重复,在任何环境中重复通过。

4、自足验证,测试应有布尔值输出,不能人为比较。

5、及时,及时编写。

第十章类

10.1类的组织

1、从一组变量列表开始。公共静态常量-私有静态变量-私有实体变量-很少有公共变量。

2、公共函数跟在变量列表之后。

10.2类应该短小

1、权责。含义模糊的词:Processor、Manager、Super都是权责聚集的的情况。

2、单一权责原则。类或模块应有且只有一条加以修改的理由。

3、鉴别权责帮助我们在代码中认识并创建出更好的抽象。

4、内聚,如果类中的每个变量都被每个方法所使用,就具有极大内聚性,但这种情况不可取也不可能。

5、大函数拆成许多小函数,也是将类拆成多个小类的时机。程序会更有组织,结构更透明。

10.3为了修改而组织

1、这一部分主要是为了防止代码的修改导致与原来的逻辑不同,从而导致代码运行不一致。这里可以类比一下nsq消息的分发机制。

2、使用接口和抽象类来进行隔离修改。

第十一章系统(我们现在使用的spring框架就是为了解决系统上分级的问题)

11.1如何建造一个城市

1、这一部分讲的是在系统层级上的区分。

11.2将系统的构造与使用分开

1、软件系统应将启始过程和启始过程之后的运行逻辑分离。

2、分解main。

3、工厂,目前我们的系统都是使用spring来管理实例化对象,已经做到了系统的构造与使用分离。

11.3扩容

1、系统的扩展。

2、切面的重要性。

11.4Java代理

11.5纯Java AOP框架

第十二章迭进

12.1通过迭进设计达到整洁目的

1、简单设计规则

运行所有测试

不可重复

表达了程序员的意图

尽可能减少类和方法的数量

12.2简单设计规则1:运行所有测试

1、全面测试并持续通过所有测试。

2、紧耦合的代码,难以编写测试。(使用DI,接口和抽象工具解耦)

12.3简单设计规则2-4:重构

12.4不可重复

1、消除重复。

12.5表达力

1、代码要好理解,使用前面所讲的各种方法,能够帮助别人理解代码。

12.6尽可能少的类和方法