相对于任何宏伟愿景,对细节的关注甚至是更为关键的专业性基础。首先,开发者通过小型实践获得可用于大型实践的技能和信用度。其次,宏大建筑中最细小的部分,比如关不紧的门、有点儿没铺平的地板,甚至是凌乱的桌面,都会将整个大局的魅力毁灭殆尽。这就是整洁代码之所系。
本书「序」中的这段话完美的诠释了作者写本书的意义。(简评在最后)
有人也许以为,关于代码的书有点落后于时代——代码不再是问题:我们应当关注模型和需求。……扯淡!我们永远抛不掉代码,因为代码呈现了需求的细节。在某些层面上,这些细节无法被忽略或抽象,必须明确之。将需求明确到机器可以执行的细节程度,就是编程要做的事。而这种规约正是代码。
勒布朗(LeBlanc)法则:稍后等于永不(Later equals never)。
多数人都知道一幅画是好还是坏。但能分辨优劣并不表示懂得绘画。能分辨整洁代码和肮脏代码,也不意味着会写整洁代码!
Bjarne Stroustrup(C++ 语言发明者):我喜欢优雅和高效的代码。代码逻辑应当直截了当,叫缺陷难以隐藏;尽量减少依赖关系,使之便于维护;依据某种分层战略完善错误处理代码;性能调至最优,省得引诱别人做没规矩的优化,高处一堆混乱来,整洁的代码只做好一件事。
Grady Booch(《面向对象分析与设计》作者):整洁的代码简单直接。整洁的代码如同优美的散文。整洁的代码从不隐藏设计者的意图,充满了干净利落的抽象和直截了当的控制语句。
Ron Jeffries(《极限编程实施》作者):简单代码,依其重要顺序:
Ward Cunningham(Wiki 发明者):如果每个例程都让你感到深合己意,那就是整洁代码。如果代码让编程语言看起来像是专为解决那个问题而存在,就可以称之为漂亮的代码。
光把代码写好可不够。必须时时保持代码整洁。
名副其实:变量、函数或类的名称应该已经答复了所有的大问题。它该告诉你,它为什么会存在,它做什么事,应该怎么用。
避免误导:应当避免使用与本意相悖的词。别用 accountList 来指称一组账号,除非它真的是 List 类型。用 accountGroup 或 bunchOfAccounts,甚至 accounts 都会好一些。
做有意义的区分:以数字系列命名(a1、a2,……aN)是依义命名的对立面。这样的名称纯属误导——完全没有提供正确信息;没有提供导向作者意图的线索。
public static void copyChars(char a1[], char a2[]) {
for (int i = 0; i < a1.length; i++) {
a2[i] = a1[i];
}
}
如果参数名改为 source 和 destination,这个函数就会像样许多。
使用读得出来的名称:
private Date genymdhms; // 生成日期,年、月、日、时、分、秒
private Date generationTimestamp;
使用可搜索的名称:窃以为单字母名称仅用于短方法中的本地变量。名称长短应于其作用域大小相对应。
避免思维映射:不应当让读者在脑中把你的名称翻译为他们熟知的名称。
类名:类名和对象名应该是名词或名词短语,如 Customer、WikiPage。避免使用 Manager、Data 这样的类名。
方法名:方法名应当是动词或动词短语,如 postPayment、deletePage 或 save。
每个概念对应一个词:给每个抽象概念选一个词,并且一以贯之。
别用双关语:避免将同一单词用于不同目的。
函数的第一规则是要短小。第二条规则是还要更短小。
函数应该做一件事。做好这件事。只做这一件事。
别害怕长名称。长而具有描述性的名称,要比短而令人费解的名称好。长而具有描述性的名称,要比描述性的长注释好。
最理想的参数数量是零(零参数函数),其次是一(单参数函数),再次是二(双参数函数),应尽量避免三(三参数函数)。如果函数看来需要两个、三个或三个以上参数,就说明其中一些参数应该封装为类了。
函数要么做什么事,要么回答什么事,但二者不可兼得。函数应该修改某对象的状态,或是返回该对象的有关信息。
重复可能是软件中一切邪恶的根源。许多原则与实践规则都是为控制与消除重复而创建。
我写函数时,一开始都冗长而复杂。有太多缩进和嵌套循环。然后我打磨这些代码,分解函数、修改名称、消除重复。我缩短和重新安置方法。有时我还拆散类。
大师级程序员把系统当作故事来讲,而不是当作程序来写。他们使用选定编程语言提供的工具构建一种更为丰富且更具表达力的语言,用来讲那个故事。
过程式代码便于在不该动既有数据结构的前提下添加新函数。面向对象代码便于在不改动既有函数的前提下添加新类。
得墨忒耳率认为,类 C 的方法 f 只应该调用以下对象的方法:
方法不应调用由任何函数返回的对象的方法。换言之,只跟朋友谈话,不与陌生人谈话。
对象曝露行为,隐藏数据。便于添加新对象类型而无需修改既有行为,同时也难以在既有对象中添加新行为。数据结构曝露数据,没有明显的行为。便于向既有数据结构添加新行为,同时也难以向既有函数添加新数据结构。
名词 | 基础定义 |
---|---|
限定资源 | 并发环境中有着固定尺寸或数量的资源。例如数据库连接和固定尺寸读/写缓存等 |
互斥 | 每一时刻仅有一个线程能访问共享数据或共享资源 |
线程饥饿 | 一个或一组线程互相等待执行结束。 |
死锁 | 两个或多个线程互相等待执行结束。 |
活锁 | 执行次序一致的线程,每个都想要起步,但发现其他线程已经「在路上」。 |
对象是过程的抽象。线程是调度的抽象。
并发是一种解耦策略。它帮助我们把做什么(目的)和何时(时机)做分解开。
并发软件的中肯说法:
生产者-消费者模型:一个或多个生产者线程创建某些工作,并置于缓存或者队列中。一个或者多个消费者线程从队列中获取并完成这些工作。生产者和消费者之间的队列是一种限定资源。
读者-作者模型:当存在一个主要为读者线程提供信息源,但只是偶尔被作者线程更新的共享资源,吞吐量就会是个问题。增加吞吐量,会导致线程饥饿和过时信息的积累。协调读者线程不去读取正在更新的信息,而作者线程倾向于长期锁定读者线程。
宴席哲学家:许多企业级应用中会存在进程竞争资源的情形,如果没有用心设计,这种竞争会遭遇死锁,活锁,吞吐量和效率低等问题。
本书后几章主要侧重于讲解 Java 代码的一些例子,对其它语言帮助不大,在这里就不做整理了。
正如我在 上一篇读书笔记 中所说的:每一本中都会充斥着许多作者的自己的观点、看法,而唯有价值观相符合或相接近的人才会觉得本书写得很不错,上一本《黑客与画家》是,这本《代码整洁之道》也是,你可能很难认为变量的命名需要有那么考究,函数的长短有那么重要,心里想着程序能运行就没事,甚至连 WARNING 都忽视掉,这类人想必并不是本书的目标群体。而本书的目标群体在开头已经注明了:你想成为一个更好的程序员。其实我觉得目标群体还可以加上一小撮人:有强迫症的程序员——比如我。
我曾经看自己四个月前的代码能羞愧得钻进地里,心想着怎么能写出这么烂的 代码 。这四个月固然有我对该门语言较高层级的数据结构更加熟悉,能更熟练的操作它们,但更多的是编程观念的改变:需要用心来写代码,不要简单敷衍了事,不要认为程序只要能运行就算成功。程序毕竟还是写给人看的,就算不是为了别人,看着意义明确的变量,缩进优美的段落,结构分明的函数,想必自己心里也会很舒畅的。