《代码整洁之道》

来源:互联网 发布:人工智能 大数据 金融 编辑:程序博客网 时间:2024/05/01 23:06

第二章 有意义的命名

2.2 名副其实:变量、函数或者类的名称要说明他们的目的。
2.3 避免误导:避免使用关键字,避免使用0 o 1 l 等。
2.4 做有意义的区分:要区分名称,就要以读者能鉴别不同之处的方式来区分。
2.5 使用读得出来的名称:要使用恰当的英语词,而不是傻乎乎的自造词。
2.6 使用可搜索的名称:避免使用单字母的名称,或者直接使用数字。
2.7 避免使用编码:带编码的名称通常不便发音,容易打错,更容易让人混淆,而且给人增加了解码的负担。(比如:匈牙利命名法)
2.8 避免思维映射:
2.9 类名:类名应当是名词或者名词短语。类名不应当是动词。
2.10 方法名:方法名应当是动词或者动词短语。
2.11 别扮可爱:名称不能太耍宝。宁可明确,勿为好玩。
2.12 每个概念对应一个词:给每个抽象概念选一个词,并且一以贯之。
2.13 别用双关语:避免将同一单词用于不同母的。同一术语用于不同概念,基本上就是双关语。
2.14 使用解决方案领域名称:只有程序员会读你的代码。
2.15 使用源自所涉问题领域的名称:
2.16 添加有意义的语境:需要用良好命名的类、函数或者名称空间来放置名称,给读者提供语境,如果没有这么做那就添加前缀说明吧。
2.17 不要添加没用的语境:只要短名称足够清楚,就要比长名称好。别给名称添加不必要的语境。

第三章 函数

3.1 短小:函数的第一原则是要短小。第二条规则是还要更短小。(我认为短小是没错,但是要在干单一事情的前提下,把函数拆分,我觉得是不合理的)
3.2 只做一件事:函数应该做一件事。做好这件事。只做这一件事。
3.3 每个函数一个抽象层次:要确保函数只做一件事,函数中的语句都要在同一抽象层级上。
3.4 switch语句:switch天生就是干N件事情。用多态实现switch我觉得使用看情况的,不能盲目的将switch转换为多态。
3.5 使用描述性的名称:如果每个例程都让你感到深合己意,那就是整洁代码。
3.6 函数参数:函数参数要尽量少,如果函数参数多余3个,则应该考虑创建一个新类了。参数起名要清晰的说明参数的意图。
3.7 无副作用:副作用是一种谎言。函数承诺只做一件事,但还是会做其他被隐藏起来的事。
3.8 分隔指令与询问:函数要么做什么,要么回答什么,但是二者不可兼得。函数应该修改某对象的状态,或者返回某对象的信息。两样都干常会导致混乱。
3.9 使用异常替代返回错误码:使用异常代替错误码,错误处理本身就是一件事。错误码枚举类型会成为磁铁需要所有需要处理错误的文件包含它。
3.10 别重复自己:重复是软件中一切邪恶的根源。
3.11 结构化编程:每个函数中的买个代码块都应该有一个入口一个出口。

第四章 注释 (别给糟糕的代码加注释---重新写吧)

注释的恰当用法是弥补我们在用代码表达意图时的失败。
4.1 注释不能美化糟糕的代码:与其花时间写注释解释你的代码,还不如花时间重新整理你的代码。
4.2 用代码来阐述:尽量少写注释,用更形象的命名来取代注释。
4.3 好注释:有些注释是必须的,也是有利的。

(1). 法律信息:为程序提供法律注释。
(2). 提供信息的注释:用注释提供基本信息。
(3). 对意图的解释:提供现有代码后续的意图。
(4). 阐释:把晦涩难明的事情阐述清楚。
(5). 警示:用于警告其他程序员某种后果。
(6). TODO注释:在代码中放置工作列表。注意:要定期检查 然后修护。
(7). 放大:注释可以放大某种看来不合理之物的重要性。
(8). 公共API的DOC:对公共API做的DOC。

4.4 坏注释:坏注释是槽糕代码的支撑或借口,或是对错误决策的修正。

(1). 喃喃自语:不要对程序加无用的注释,那样只会自说自话。甚至让人误解代码的真正意图。
(2). 多余的注释:多余的注释使读程序的难度更大。
(3). 误导性注释:误导的注释容易使使用你的代码的人陷于调试的困境中。
(4). 循环性注释:简单的只阐述方法名,意图明确的参数或者作者等信息的注释,不需要总是放在每个函数前面。
(5). 日志式注释:每修改一次就在文件开始处记录修改内容,修改日期等的日志方式。在有版本控制软件的今天这个已经是多余。
(6). 废话注释:对于显然之事喋喋不休。
(7). 可怕的废话:代码作者在复制代码时把注释也复制过来,但是结果代码修改了,而注释没有修改。
(8). 能用函数或变量时就别用注释:
(9). 位置标记:用来标记位置的特殊注释,一定要清理。
(10). 括号后面的注释:放在长循环,或者长if比括号后,指示是那个语句的结束括号。如果你想在右括号后加注释,那你应该考虑下写个短小的函数。
(11). 归属与署名:代码版本控制软件是这类注释的最佳归属。
(12). 注释掉的代码:用版本控制软件做历史记录。把已经不需要的代码立即删掉。
(13). HTML注释:还有这种注释吗,天啊。
(14). 非本地信息:把注释加到离目标代码最近的地方。而不是加到远离目标代码的位置。否则目标代码被修改时,注释必然会被忘记修改。这是噩梦的开始。
(15). 信息过多:别在注释中添加有趣的历史性话题或者无关的细节描述。
(16). 不明显的联系:注释及其描述的代码之间的联系应该是显而易见。如果你不嫌弃麻烦要写注释,至少让读者能看着注释和代码,并且理解注释所谈何物。
(17). 函数头:为短函数选个好命,显然要比函数前加注释要好。
(18). 非公共代码的DOC:显然非公共代码API不需要DOC。

第五章 格式

5.1 格式的目的:为将来的修改减少阻力。
5.2 垂直格式:短文件比长文件好理解。

(1). 向报纸学习:名称简单一目了然;源文件顶部给出高层次概念和算法;细节应该从上到下逐次展开。
(2). 概念间垂直方向上的区隔:每个函数、每个功能块之间用空行隔开。
(3). 垂直方向上的靠近:紧密相关的代码应该互相靠近。
(4). 垂直距离:变量声明应尽可能靠近使用位置。实体变量应该统一放在统一地方(C++统一放在类底部)。相关函数应该离得尽可能的靠近。概念相关的代码应该放在一起。
(5). 垂直顺序:一般使用自上而下的顺序。函数调用尽量遵从。

5.3 横向格式:每行字符个数应该遵从不需要拉动滚动条的原则。推荐120个字符。

(1). 水平方向上的区隔与靠近:使用空格将不相关的东西分隔。使用空格把相关的东西紧密联系。(厄,好别扭的说法)
(2). 水平对齐:不要刻意去水平对齐,这样只会让读者的注意力分散在不重要的东西上。
(3). 缩进:层级之间一定要缩进固定宽度。
(4). 空范围:尽量不要写只有一行的for 或者while,如果实在没有办法,那就把分号写到下一行,让读者重视这里。

5.4 团队规则:如果团队有规则则要遵从团队规则。

第六章 对象和数据结构

6.1 数据抽象:取值函数和赋值函数并不是一个简简单单的函数那么简单,它代表了数据抽象以及数据封装的思想。实现和应用是隔离的。
6.2 数据、对象的反对称性:对象把数据隐藏于抽象之后,暴露操作数据的函数;数据结构暴露其数据,没有提供有意义的函数。对象和数据结构之间的二分原理:过程式代码便于在不改动即有数据结构的前提下添加新函数。面向对象代码便于在不改动即有函数的前提下添加新类。也即:过程式代码难以添加新数据结构,因为必须修改所有函数。面向对象代码难以添加新函数,因为必须修改所有类。
6.3 得墨忒耳律(The Law Of Demeter):模块不应了解他所操作对象的内部情形。也即方法不应调用由任何函数返回的对象的方法。

(1). 火车失事:不要直接对函数返回的对象调用方法。声明一个对象将函数返回的对象赋值给它,然后再该对象的方法。
(2). 混杂:不要把对象和数据结构混在在一起使用。这样你即得不到数据结构的好处也得不到对象的好处。
(3). 隐藏结构:

6.4 数据传送对象:只有公共变量没有函数的类叫做数据传送对象。用于传递参数等。

 

第七章 错误处理

7.1 使用异常而非返回码:返回错误码需要立刻检查错误码,但是程序员往往忘记检查,因此最好是使用异常。
7.2 先写try catch finally语句:这样可以在程序中定义一个范围,而这个范围中的错误可以被系统捕获。即使你不做任何处理。
7.3 我们不可控异常:
7.4 给出异常发生的环境说明:在抛出异常的时候要提供详细的发送说明。
7.5 依调用者需要定义异常类:
7.6 定义常规流程:
7.7 不要返回null值:在任何返回null值得地方都要进行判定,如果有一处没有判定那代码就将失控。
7.8 不要传递null值:传递null值意味着每一处传递的地方都要进行判定,如果有遗忘的地方那代码同样会失控。

 

第八章 边界

8.1 使用第三方代码:第三方代码或多或少都会有些限制,要注意这些限制。有可能下一版本中就会有改变呢。
8.2 浏览和学习边界:
8.3 这一条款的电子书页缺失
8.4 学习性测试的好处不只是免费:
8.5 使用尚不存在的代码:
8.6 整洁的边界

第九章 单元测试

9.1 TDD三定律:在编写不能通过的单元测试前,不可编写生产代码;只可编写刚好无法通过的单元测试,不能编译也算不能通过;只可编写刚好足以通过当前失败测试的生产代码。
9.2 保持测试整洁:测试代码要和生产代码一样整洁。测试代码和生产代码一样的重要。
9.3 整洁的测试:测试代码的要遵从的就是可读性。比生产代码更要可读。
9.4 每个测试一个断言:每个测试函数只测试一个概念。
9.5 F.I.R.S.T:快速(Fast) 测试应该够快、 独立(Independent) 测试之间应该相互独立、可重复(Repeatable) 测试应该可在任何环境重复通过、自足验证(Self-validating) 测试应该有布尔值输出 、及时(Timely) 测试应及时编写。

第十章 类

10.1 类的组织:次序先公用方法,保护方法,私有方法,共有数据,保护数据,私有数据。
10.2 类应该短小:类中的共有方法不应该过多。

(1). 单一职权原则:系统应该由许多短小的类而不是几个庞大的类组成。每个小类封装一个权责,每个小类只有一个修改的理由。
(2). 内聚:内聚性高意味着类中的方法和变量互相依赖,互相组合成一个整体。
(3). 保持内聚性就会得到许多短小的类:当类丧失内聚性的时候就拆分它。

10.3 为了修改而组织:

第十一章 系统

11.1 如何建造一个城市:
11.2 将系统的构造和使用分开:构造和使用不是一样的事情。

(1). 分解main:将全部的构造过程放到main函数中。
(2). 工厂:管理构造过程,把构造过程封装到这里。
(3). 依赖注入:分离构造与使用的强大机制。

11.3 扩容:
11.4 Java代理:
11.5 纯Java AOP框架:
11.6 AspectJava的方面:
11.7 测试驱动系统架构:将架构按需从简单演化到精细。没有必要先做大设计(Big Design Up Front),BDUF甚至是有害的,它阻碍改进,因为心理上抵制丢弃即成之事。也因为框架上的选择影响后续的设计思路。
11.8 优化决策:提前决策是一种预备知识不足的决策。
11.9 明智使用添加了可论证价值的标准:
11.10 系统需要领域特定语言:使用领域特定语言可以提高效率。

第十二章 迭进

12.1 通过迭代设计达到整洁目的:运行所有测试;不可重复;表达了程序员的意图;尽可能减少类和方法的数量;遵循以上四条对良好设计有莫大帮助。
12.2 简单设计规则1 运行所有测试:运行的测试越多,说明系统更健壮。
12.3 简单设计规则2~4 重构:对有坏味道的代码及早进行重构,有利于以后的维护。也会让系统变的更明确。
12.4 不可重复:重复是拥有良好设计系统的打敌。重复意味着额外的工作,额外的风险以及不必要的复杂度。
12.5 表达力:代码应当清晰表达作者的意图。代码写的越清晰,维护人员花的时间就越少,从而减少缺陷,减少维护成本。其实维护人员最大可能就是自己,清晰代码,是对自己以后工作的减负。
12.6 尽可能少的类和方法:在保持类和函数短小的情况下尽可能的减少类和函数的数量。

第十三章 并发编程

对象是过程的抽象,线程是调度的抽象。
13.1 为什么要并发:最大限度的使用CPU资源,提高工作效率(自己的观点)。书中对并发的说法:并发会在性能和编写一些额外代码上增加一些开销;正确的并发是复杂的,即便对于很简单的问题也是如此;并发缺陷并非总能重现,所以常被看做偶发事件而忽略。并发常常需要对设计策略进行根本性修改。
13.2 挑战:并发会引发一系列问题。死锁,竞争,饥饿。。。等等。
13.3 并发防御原则:

(1). 单一职权原则:分离并发相关代码以及一般代码。
(2). 推论--限制数据作用域:谨记数据封装;严格限制对可能被共享的数据的访问。
(3). 推论--使用数据副本:避免数据共享的好方法之一就是一开始就避免数据共享。
(4). 推论--线程应尽可能的独立:尝试将数据分解到可被独立线程操作的独立子集。

13.4 了解Java库:
13.5 了解执行模型:根据并发应出的问题可以有如下模型

(1). 生产者-消费者模型:一个或几个线程生产消息放入到共享队列中,然后一个或几个线程消费消息从共享队列。共享队列是一种限定资源。
(2). 读者-作者模型:
(3). 宴席哲学家:

13.6 警惕同步方法之间的依赖:避免使用一个共享对象的多个方法。
13.7 保持同步区域微小:尽可能的减小同步区域。
13.8 很难编写正确的关闭代码:尽早考虑关闭问题,尽早另其工作正常。
13.9 测试线程代码:编写有潜力暴露问题的测试,在不同的编程配置、系统配置和负载条件下频繁的运行。

(1). 将伪失败看做可能的线程问题:不要将系统错误归咎于偶发事件。
(2). 先使非线程代码可工作:不要同时追踪线程缺陷和非线程缺陷。确保代码在线程之外可工作。
(3). 编写可插拔的线程代码:编写可插拔的线程代码,这样就可以在不同的配置环境下运行。
(4). 编写可调整的线程代码:在编写过程中要随时可改动线程的数量以及自我调整。
(5). 编写多于处理器数量的线程:系统在切换任务时发生一些事情,为了促使任务切换的发生,运行多处理器核心数量的线程。频繁的任务切换,可提高发现缺陷的几率。
(6). 在不同平台上运行:尽早并经常地在所有目标平台上运行代码。
(7). 装置试错代码:可以放置sleep、wait、yield、priority等操作,可以提高发现缺陷的几率。
(8). 硬编码:找到合适的位置放置上面条款中的四个函数。
(9). 自动化:使用异动策略搜出错误。

 

第十四章 逐步改进

14.1 Args的实现:要想写整洁的代码,必须先写脏代码,然后整理它。多数新手刚开始就只写能工作的代码,然后继续写下面的功能,而那些能工作的代码,仅仅保持在能工作而已。
14.2 Args草稿:简单的系统通过多次的改动会变的庞大,杂乱。这时候你应该停下来,重构代码。
14.3 字符串参数:

第十五章 JUnit内幕

第十六章 重构SerialDate

第十七章 味道与启发

代码的坏味道,与<<重构>>那本书中,有内容重叠。与前面的章节中的内容也有重叠。
17.1 注释:

C1:不恰当的注释。
C2:废弃的注释。
C3:冗余的注释。
C4:槽糕的注释。
C5:注释的代码。

17.2 环境:

E1:需要多步才能实现的构建。
E2:需要多步才能做到的测试。

17.3 函数:

F1:过多的参数。
F2:输出参数。
F3:标识参数。
F4:死函数。

17.4 一般性问题:

G1:一个源文件中存在多种语言。
G2:明显的行为未被实现:最小惊异原则(函数或者类应该实现其他程序员有理由期待的行为)。
G3:不正确的边界行为:不要依赖直觉。
G4:忽视安全:切尔诺贝利核电站爆炸了,因为电厂经理一条又一条的忽略了安全机制。
G5:重复。
G6:在错误的抽象层级上的代码。
G7:基类依赖于派生类。
G8:信息过多。
G9:死代码。
G10:垂直分隔。
G11:前后不一致。
G12:混淆视听。
G13:人为耦合:不要因为方便把代码随便放置在别的类。
G14:特性依恋。
G15:选择算子参数。
G16:晦涩的意图。
G17:位置错误的权责。
G18:不恰当的静态方法。
G19:使用解释性变量。
G20:函数名称应该表达其行为。
G21:理解算法。
G22:把逻辑依赖改为物理依赖。
G23:用多态替代if/else switch/case。
G24:遵循标准约定。
G25:用命名常量来代替魔术数。
G26:准确:对于代码中的每一处都要做到准确。
G27:结构基于约定。
G28:封装条件。
G29:避免否定性条件。
G30:函数只该做一件事。
G31:掩蔽时序耦合。
G32:别随意:构建代码需要理由,而且理由应该与代码结构相契合。随意的代码别人想着改它,结构自始至终都一致的别人会去用它。
G33:封装边界条件:边界条件的检查应该封装在一起,而不是散落在代码各处。
G34:函数应该只在一个抽象层级上。
G35:在较高层级放置可配置数据。
G36:避免传递浏览:这条前面也有,这里的这个名字起的很含糊。其实很简单就是不要调用函数返回对象的函数。

17.5 Java:

17.6 名称:

N1:采用描述性名称。
N2:名称应与抽象层级相符。
N3:尽可能使用标准命名法。
N4:无歧义的名称。
N5:为较大作用范围选用较长的名字。
N6:避免编码。
N7:名称应该说明副作用。

17.7 测试:

T1:测试不足。
T2:使用覆盖率工具。
T3:别略过小测试。
T4:被忽略的测试就是对不确定事情的疑问。
T5:测试边界条件。
T6:全面测试相近缺陷。
T7:测试失败的模式有启发性。
T8:测试覆盖率的模式有启发性。
T9:测试应该快速。

完美之道,不在于无可增加,而在于无可减少。
原创粉丝点击