《代码整洁之道》阅读笔记

第1章 整洁代码

1.1 要有代码

1.2 糟糕的代码

Later equals never.

1.3 混乱的代价

1.4 思想流派

第2章 有意义的命名

2.1 介绍

2.2 名副其实

        名称应该告诉你:它为什么存在?它做什么事?应该怎么用

2.3 避免误导

        程序员必须表面留下掩藏代码本意的错误线索,应当避免使用与本意相悖的词。

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 不要添加没用的语境

2.18 最后的话

        取好名字最难的地方在于需要良好的描述技巧和共有文化背景

第3章 函数

3.1 短小

        每个函数都只说一件事,每个函数都依序把你带到下一个函数

3.2 只做一件事

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

3.3 每个函数一个抽象层级

        自顶向下读代码:向下规则

        To include setups and teardowns, we include setups, then we include the test page content, and then we include the teardowns.

        To include the setups, we include the suite setup if this is a suite, then we include the regular setup.

        To include the suite setup, we search the parent hierarchy for the "SuiteSetUp" page and add an include statement with the path of that page.

        To search the parent...

3.4 switch语句

3.5 使用描述性的名称

       

3.6 函数参数

         最理想的参数数量为0

3.7 无副作用

3.8 分隔指令与询问

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

3.10 别重复自己

3.11 结构化编程

3.12 如何写出这样的函数

3.13 小结

        每个系统都是使用某种领域特定语言搭建,而这种语言是程序员设计来描述那个系统的。函数是语言的动词,类是名词。这并非是退回到那种认为需求文档中的名词和动词就是系统中类和函数的最初设想的可怕的旧观念。其实这是个历史更久的真理。编程艺术是且一直就是语言设计的艺术。

        大师级程序员把系统当作故事来讲,而不是当作程序来写。他们使用选定编程语言提供的工具构建一种更为丰富且更具表达力的语言,用来将那个故事。那种领域特定语言的一个部分,就是描述在系统中发生的各种行为的函数层级。在一种狡猾的递归操作中,这些行为使用它们定义的与领域紧密相关的语言讲述自己那个小故事。

        本章所讲述的是有关编写良好函数的机制。如果你遵循这些规则,函数就会短小,有个好名字,而且被很好地归置。不过永远别忘记,真正的目标在于讲述系统的故事,而你编写的函数必须干净利落地瓶装到一起,形成一种精确而清晰的语言,帮助你讲故事。

第4章 注释

        什么也比不上放置良好的注释来得有用。什么也不会比乱七八糟的注释更有本事搞乱一个模块。什么也不会比陈旧、提供错误信息的注释更有破坏性。

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

4.2 用代码来阐述

4.3 好注释

4.4 坏注释

第5章 格式

5.1 格式的目的

5.2 垂直格式

5.3 横向格式

5.4 团队规则

5.5 鲍勃大叔的格式规则

第6章 对象和数据结构

        将变量设置为私有(private)有一个理由:我们不想其他人依赖这些变量。我们还想再心血来潮时能自由修改其类型或实现。那么,为什么还是有那么多程序员给对象自动添加赋值器和取值器,将私有变量公之于众,如同它们根本就是公共变量一般呢?

6.1 数据抽象      

6.2 数据、对象的反对称性

6.3 得墨忒耳律

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

6.4 数据传送对象

6.5 小结

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

        在任何系统中,我们有时会希望能够灵活地添加新数据类型,所以更喜欢在这部分使用对象。另外一些时候,我们希望能灵活地添加新行为,这时我们更喜欢使用数据类型和过程。优秀的软件开发者不带成见地了解这种情形,并依据手边工作的性质选择其中一种手段。

第7章 错误处理

7.1 使用异常而非返回码

返回码:调用之后立即检查错误

异常:逻辑步骤和错误处理隔离,更直观整洁

7.2 先写try-catch-finally语句

        执行 try-catch-finally 语句中的 try 部分代码事,表明可随时取消执行,并在 catch 语句中连续。

        在某种意义上,try 代码块就像是事务;catch 代码块将程序维持在一中持续状态,无论 try 代码块中发生了什么均如此。所以,在编写可能抛出异常的代码时,最好先写出 try-catch-finally 语句。这能帮你定义代码的用户应该期待什么,无论 try 代码块中执行的代码出什么错都一样。

7.3 使用不可控异常

        可控异常(checked exception)的代价是违反开闭原则

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

        你抛出的每个异常,都应当提供足够的环境说明,以便判断错误的来源和处所。在Java中,可以从人物异常里得到堆栈踪迹(stack trace);然而,堆栈踪迹却无法告诉你该失败操作的初衷。

        应创建信息充分的错误消息,并和异常一起传递出去。在消息中,包括失败的操作和失败类型。如果你的应用程序有日志系统,传递足够的信息给catch块,并记录下来。

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

7.6 定义常规流程

7.7 别返回null值

7.8 别传递null值

7.9 小结

        整洁代码是可读的,但也要顽固。顽固与可读并不冲突。如果将错误处理隔离看待,独立于主要逻辑之外,就能写出顽固而整洁的代码。做到这一步,我们就能单独处理它,也极大地提升了代码的可维护性。

第8章 边界

8.1 使用第三方代码

         

8.2 浏览和学习边界

8.3

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

8.5 使用尚不存在的代码

8.6 整洁的边界

第9章 单元测试

9.1 TDD三定律

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

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

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

9.2 保持测试整洁

        测试代码和生产代码一样重要。它需要被思考、被设计和被照料,它该像生产代码一般保持整洁。

9.3 整洁的测试

        整洁的测试:可读性、可读性、可读性,明确,简洁,有足够的的表达力。

        构造-操作-检验(BUILD-OPERATE-CHECK)模式:每个测试都清晰地拆分为三个环节,第一个环节是构造测试数据;第二个环节是操作测试数据;第三个环节是检验操作是否得到期望的结果。

9.4 每个测试一个断言

        每个测试函数只测试一个概念

9.5 F.I.R.S.T

        整洁的测试应该遵循以下5条规则:

        1. 快速(Fast):测试应该够快。

        2. 独立(Independent):测试应该相互独立。

        3. 可重复(Repeatable):测试应当可在任何环境中重复通过。

        4. 自足验证(Self-Validating):测试应该有布尔值输出。

        5. 几时(Timely):测试应几时编写。

9.6 小结

        对于项目的健康度,测试盒生产代码同等重要。或许测试更为重要,因为它保证和增强了生产代码的可扩展性、可维护性和可复用性。所以,保持测试整洁吧。让测试具有表达力并短小精悍。发明作为面向特定领域语言的测试API,帮助自己编写测试。

        如果你坐视测试腐坏,那么代码也会跟着腐坏。保持测试整洁吧。

第10章 类

        到目前为止,本书一直在讨论如何编写良好的代码行和代码块。我们深入研究了函数的恰当构成,以及函数之间如何关联。不过,尽管讨论了这么多关于代码语句及由代码语句构成的函数的表达力,除非我们将注意力放到代码组织的更高层面,就始终不能得到整洁的代码。

10.1 类的组织

        类应该从一组变量列表开始,顺序为:公共静态常量,私有静态变量,私有实体变量

        公共函数应该跟在变量列表之后。我们喜欢把由某个公共函数调用的私有工具函数紧随在公共函数后面。这也符合了自顶向下原则,让程序读起来更像一篇报纸文章。

10.2 类应该短小

        类的名称应当描述其权责(responsibility)

  1. 单一权责原则(Single Responsibility Principle,SRP):类或模块应有且只有一条加以修改的理由。系统应该由许多短小的类而不是少量巨大的类组成。每个小类封装一个权责,只有一个修改的原因,并与少数其他类一起协同达成期望的系统行为。
  2. 内聚:类应该只有少量实体变量。类中的每个方法都应该操作一个或多个这种变量。
  3. 保持内聚性就会得到许多短小的类

10.3 为了修改而组织

        隔离修改:具体类包含实现细节,抽象类则只呈现概念。依赖于具体细节的客户类,当细节改变时,就会有风险。我们可以借助接口和抽象类来隔离这些细节带来的影响。

第11章 系统

11.1 如何建造一个城市

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

11.3 扩容

11.4 Java代理

11.5 纯Java AOP框架

11.6 AspectJ的方面

11.7 测试驱动系统架构

        最佳的系统架构由模块化的关注面领域组成,每个关注面均用纯Java对象实现。不同的领域之间用最不具有侵害性的方面或类方面工具整合起来,这种架构能测试驱动,就像代码一样。

11.8 优化策略

11.9 明智使用添加了可论证价值的标准

11.10 系统需要领域特定语言

11.11 小结

        系统也应该是整洁的。侵害性架构会湮灭领域逻辑,冲击敏捷能力。当领域逻辑受到困扰,质量也就堪忧,因为缺陷更容易隐藏,用户故事更难实现。当敏捷能力受到损害时,生产力也会降低,TDD的好处遗失殆尽。

        在所有的抽象层级上,意图都应该清晰可辩。只有在编写POJO并使用类方面的机制来无损地组合其他关注面时,这种事情才会发生。

        无论是涉及系统或单独的模块,别忘了使用大概可工作的最简单方案。

第12章 迭进

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

  •  运行所有测试
  • 不可重复
  • 表达了程序员的意图
  • 尽可能减少类和方法的数量

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

        设计必须制造出如预期一般工作的系统。

12.3 简单设计规则--重构

        在重构过程中,可以应用有关优秀软件设计的一切知识。提升内聚性,降低耦合度,切分关注面,模块化系统性关注面,缩小函数和类的尺寸,选用更好的名称,如此等等。这也是应用简单设计后三条规则的地方:消除重复,保证表达力,尽可能减少类和方法的数量

12.4 不可重复

        重复是拥有良好设计系统的大敌。它代表了额外的工作、额外的风险、额外的且不必要的复杂度

12.5 表达力

12.6 尽可能少的类和方法

第13章 并发编程

        对象是过程的抽象,线程是调度的抽象。

13.1 为什么要并发

        并发是一种解耦策略。它帮助我们把做什么(目的)和何时做(时机)分解开。

        解耦目的与时机能够明显地改进应用程序的吞吐量和结构。

       常见并发编程的迷思和误解:

        (1)并发总能改变性能:并发有时能改进性能,但只在多个线程或处理器之间能分享大量等待时间的时候管用。

        (2)编写并发程序无需修改设计:事实上,并发算法的设计有可能与单线程系统的设计极不相同。目的与时机的解耦往往对系统结构产生巨大影响。

        (3)在采用Web和EJB容器的时候,理解并发问题并不重要:实际上,你最好了解容器在做什么,了解如何对付本章后文将提到的并发更新、死锁等问题。

        下面是一些有关编写并发软件的中肯说法:

        (1)并发会在性能和编写额外代码上增加一些开销;

        (2)正确的并发是复杂的,即便对于简单的问题也是如此;

        (3)并发缺陷并非总能重现,所以常被看做偶发事件而忽略;

        (4)并发尝尝性需要对设计策略的根本性修改。

13.2 挑战

13.3 并发防御原则

(1)单一权责原则

        单一权责原则(SRP)认为,方法/类/组件应当只有一个修改的理由。并发设计自身足够复杂到成为修改的理由,所以也该从其他代码中分离出来。不幸的是,并发实现细节常常直接嵌入到其他生产代码中。下面是要考虑的一些问题:

  • 并发相关代码有自己的开发、修改和调优生命周期;
  • 开发相关代码有自己要对付的挑战,和非并发相关代码不同,而且往往更为困难;
  • 即便没有周边应用程序增加的负担,写得不好的并发代码可能的出错方式数量也已经足具挑战性。

        建议:分离并发相关代码与其他代码        

(2)限制数据作用域

        采用synchronized关键字在代码中保护一块使用共享对象的临界区(critical section)

        建议:谨记数据封装;严格限制对可能被共享的数据的访问

(3)使用数据复本

        避免共享数据的好方法之一就是一开始就避免共享数据。假设使用对象复本能避免代码同步执行,则因避免了锁定而省下的价值有可能补偿得上额外的创建成本和垃圾收集开销。

(4)线程应尽可能地独立

        尝试将数据分解到可被独立线程(可能在不同处理器上)操作的独立子集

13.4 了解Java库

13.5 了解执行模型

  • 限定资源:并发环境中有着固定尺寸或数量的资源。例如数据库连接和固定尺度读/写缓存等。
  • 互斥:每一时刻仅有一个线程能访问共享数据或共享资源。
  • 线程饥饿:一个或一组线程在很长时间内或永久被禁止。例如,总是让执行得快的线程先运行,假如执行得快的线程没完没了,则执行时间长的线程就会“挨饿”。
  • 死锁:两个或多个线程互相等待执行结束。每个线程都拥有其他线程需要的资源,得不到其他线程拥有的资源,就无法终止。
  • 活锁:执行次序一致的线程,每个都想要起步,但发现其他线程已经“在路上”。由于竞步的原因,线程会持续尝试起步,但在很长时间内却无法如愿,甚至永远无法启动。

13.6 警惕同步方法之间的依赖

13.7 保持同步区域微小

13.8 很难编写正确的关闭代码

13.9 测试线程代码

13.10 小结

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值