一、整洁代码
什么是整洁的代码??
- 优雅、效率
- 整洁的代码只做好一件事
- 减少重复代码,提高表达力、提早构建简单抽象。
- 让营地比你来时更干净
二、有意义的命名
命名规则:
1、名副其实
- 变量、函数 或者类的名称应该已经答复了所有的大问题。它应该告诉你,它为什么会存在,它做什么事,应该怎么用。如果名称需要注释来补充,那就不算是名副其实。
2、避免误导
- 程序员必现避免留下掩藏代码本意的错误线索。应当避免使用与本意相违背的词。
- 提防使用不同之处比较小的名称
3、做有意义的区分
- 不要使用错误的拼写做区分
- 光是添加数字系列或者废话远远不够
- 体现有意义的区分
4、使用读得出来的名称
5、使用可以搜素的名称
- 名称长短应该与其作用域大小相对应
- 长名称胜于短名称,搜得到的名称胜于用自造编码代写就的名称
6、避免使用编码
7、避免使用思维映射
8、类名
类名称和对象名称应该是名词或者名词短语,避免使用manager、processor、Data、Info这样的类名。类名不应当是动词。
9、方法名
方法名应该是动词或者动词短语,如postPayment、deletePage、或者save。属性访问器、修改器或者断言应该根据其值命名,并依javaBean标准加get、set、is前缀。
10、别扮可爱
11、每个概率对应一个词
12、别用双关语
避免将同一个单词用于不同的目的。
一词一义
13、使用解决方案领域名称
只有程序员才会读你的代码,所以,尽量使用那些计算机科学的术语、算法名、模式名、数学术语
14、使用源自所涉问题领域的名称
15、添加有意义的语境
- 需要用良好命名的类、函数、或者名称空间来放置名称、给读者提供语境。
- 如果没有那么做,给名称添加前缀就是最后一招了。
16、不要添加没用的语境
取好名字最难的地方在于需要良好的描述技巧和共有文化背景,与其说这是一种技术、商业或者管理问题,还不如说是一种教学问题。
三、函数
1、短小
- 函数的第一规则是短小,第二条规则还是短小
- 经过漫长的试错,经验告诉我们,函数就应该短小
- 函数的缩进层级不该多于一层或者两层,这样的函数易于阅读和理解
- if语句、else语句、whlie语句等,其中的代码块应该只有一行,该行大抵应该是一个函数调用语句,这样不但能保持函数的短小,而且,因为块内调用的函数拥有教具体的说明性的名称,从而增加了文档上的价值。
2、只做一件事
- 函数应该做一件事情。做好一件事情。只做一件事情。
- 只做一件事情的函数无法被合理地切分为多个区段。
- 如果函数可以再拆出一个函数,说明该函数不止做了一件事情。
3、每个函数一个抽象层级
- 要确保函数只做一件事情,函数中的语句都要在同一抽象层级上。
- 自顶向下读代码:向下规则。我们想要这样的程序:程序就像一系列TO起头的段落,每一段都描述当前抽象层级,并引用位于下一抽象层级的后续TO起头的段落。
- 一旦细节与基础概念混杂,更多的细节就会在函数中纠结起来。
4、switch语句
- 尽量让switch都埋藏在较底层的抽象层级,而且永不重复
5、使用描述性的名称
- 好名称的价值怎么好评都不为过
- 如果每个例程都让你感到深合己意,那就是整洁代码
- 别害怕长名称。长而具有描述性的名称,要比短而令人费解的名称好,要比描述性的长注释好
- 使用某种命名约定,让函数名称中的多个单词容易阅读,然后使用这些词给函数取个能说清其功能的名称
- 别害怕花时间取名字。
- 选择描述性的名称能理清你关于模块的设计思路,并帮你改进之。追索好名称,往往导致对代码的改善重构。
- 命名方式要保持一致。使用与模块名一脉相承的短语、名称和动词给函数命名。
6、函数参数
- 最理想的参数数量是零(零参函数),其次是一、再次是二,应尽量避免三
- 参数不易对付,他们带有太多概率性。
- 标识参数丑陋不堪。向函数传入布尔值简直就是骇人听闻的做法。这样做,方法签名立刻变得复杂起来,大声宣布本函数不止做一件事情。
- 两个参数的函数比一元函数难懂。如果函数看起来需要两个、三个或者以上的参数,就说明其中一些参数应该封装为类了。
7、无副作用
- 函数承诺只做一件事情,但是还是会做其他被影藏的事情。有时他会对自己类中的变量做出未能预期的改动
- 避免使用输出参数,如果函数必须要修改某种状态,就修改所属对象的状态吧。
8、分割指令与询问
- 函数要么做什么事情,要么回答什么事情,二者不可兼得。函数应该修改某个对象的状态,或者返回该对象的有关信息。两者都干常会导致混乱。
9、使用异常代替返回错误码
- 从指令式函数返回凑我吗轻微违反了指令与询问分隔的规则,它鼓励了再if语句判断中把指令当做表达式使用。
- try/catch 代码丑陋不堪,它们搞乱了代码结构,把错误处理与正常流程混为一谈,最好把try/catch 代码块的主体部分抽离出来,另外形成函数。
- 函数应该只做一件事,而错误处理就是一件事,因此,处理错误的函数不应该做其他事情。这意味着,如果关键字try在某个函数中存在,他就该是这个函数的第一个单词,而且在catch/finanlly代码后面,也不改有其他内日。
10、别重复自己
- 重复可能是软件中一切邪恶的根源,许多原则与实践规则都是为了控制与消除重复而创建。
11、结构化编程
- 结构化编程意味着在每个函数中只应该由一个return语句,循环总不能有break和contunue,。但是对小函数,这些规则帮助不大,只有在大函数中,这些规则才会有明显的好处。
12、如何写出这样的函数
- 写代码和写别的东西很像,你先想什么就写什么,然后再打磨它,初稿也许粗陋无序,你就斟酌推敲,直到达到你心目中的样子。
- 没有人从一开始就按照规则写函数的
- 配上一套单元测试,覆盖每一行丑陋的代码
13、小结
- 函数是语音的动词,类是名词
- 大师级程序员把系统当做故事来讲,而不是当做程序来写。
- 本章所讲述的是有关编写良好函数的机制,如果你遵守这些规则,函数就会短小,有个很好的名字,而且被很好的归置。不过永远别忘记,真正的目标在于讲述系统的故事。2️⃣你编写的函数必须干净利落的拼接到一起,形成精确而清晰的语言,帮助你讲故事。
四、注释
- 注释的恰当的用法是弥补我们在用代码表达意图时遭遇的失败。
- 注释并不总是随着代码变动的,导致注释存在的越久,就离其所描述的代码越远。
- 把力气用在写清除代码上,直接保证无须编写注释。
- 不准确的注释要比没有注释坏得多。
1、注释不能美化糟糕的代码
- 写注释的常见动机之一是糟糕的代码的存在。带有少量注释的整洁而有表达力的代码,要比带有大量注释的零碎而负责的代码像样的多。
2、用代码来阐述
- 有时候,代码不足以解释行为,
- 很多时候,只需要创建一个描述与注释所言同一事物的函数即可
- 唯一真正好的注释是你想办法不去写的注释。
3、好注释
3.1 法律信息
有时,公司代码规范要求编写与法律有关的注释,如,版权及著作生命就是必须和有理由在每个源文件开头注释处放置的内容。
3.2 提供信息的注释
有时用注释来提供基本信息也有其用处,例如,解释某个抽象方法的返回值。
3.3对意图的解释
3.4 阐释
有时,注释把某些晦涩难懂的参数或者返回值的意义翻译为某种可读形式,也会是有用的。
3.5警告
3.6TODO注释
3.7放大
注释可以用来放大某种看起来不合理之物的重要性。
3.8公用API中的javaDoc
4、坏注释
坏注释都是糟糕代码的支撑或者借口,或者对错误决策的修正,基本上等于程序员自说自话。
如果只是因为你觉得应该或者因为过程需要就添加注释,那就是无谓之举。如果你决定写注释,就要花必要的时间确保写出最好的注释。
4.1 喃喃自语
4.2 多余的注释
4.3误导性的注释
4.4循规式注释
4.5 日志式注释
4.6废话式注释
4.7可怕的废话
4.8能用函数或者变量时就别用注释
4.9标记位置
4.10 括号后面的注释
4.11 归属与署名
4.12注释掉的代码
4.13Html注释
4.14非本地信息
4.15信息过多
4.16不明显的联系
4.17函数头
4.18非公共代码中的javadoc
4.19 范例
五、格式
代码格式很重要,代码格式不可忽略,必须严肃对待。代码格式关乎沟通,而沟通是专业开发者的头等大事。
你今天编写的功能,极有可能在下个版本中被修改,但代码的可读性却会对以后可能发生的修改行为产生深远的影响。原始代码修改之后很久,其代码风格和可读性仍会影响到可维护性和可扩展性。即使你的代码已不复存在,你的风格和律条仍存活下来。
1、垂直格式
1.1 短文件通常较长文件易于理解
1.2向报纸学习
有一个头条,告诉你故事主题,
第一段是整个故事的大纲,给出粗线条概述,但隐藏了故事细节
接的读下去,细节渐次递增,直到你了解了所有的日期、名字、引语、说法以及其他的细节
1.3概率间垂直方向上的区隔 -分割行
1.4垂直方向上的靠近
紧密相关的代码应该相互靠近。
1.5垂直距离
对于哪些关系密切、放置在同一源文件中的概率,它们之间的区隔应该成为对相互的易懂度有多重要的衡量标准,应避免迫使读者在源文件和类中跳来跳去
若某个函数调用了另外一个,就应该把它们放到一起,而且调用者应该尽可能放在被调用者的上面。
我们期望最重要的概念最先出来,期望以包括最少细节的方式表述它们,期望底层细节最后出来,这样,我们就能扫过源代码文件,自最前面的几个函数获知要旨,不至于沉溺到细节中
2、横线格式
2.1水平方向上的区隔与靠近
2.2水平对齐
2.3缩进
2.4空范围
3、团队规则
好的软件系统是由一系列读起来不错的代码文件组成的。
六、对象和数据结构
将变量设置为私有的有一个理由:我们不想其他人依赖这些变量。我们还想在心血来潮时修改其类型或者实现。
1、数据抽象
我们不愿意暴露数据细节,更愿意以抽象的形态表述数据,这并不只是用接口/或赋值器、取值器就万事大吉了,要以最好的方式呈现某个对象包含的数据,需要做严肃的思考,傻乐着乱加取值器和赋值器,是最坏的选择。
2、数据、对象的反对称性
对象把数据隐藏于抽象之后,暴露操作数据的函数,数据结构暴露其数据,没有提供有意义的函数。
过程式代码(使用数据结构的代码)便于在不修改既有的数据结构的前提下添加新的函数,面向对象代码便于在不改动既有函数的前提下添加新的类。反过来讲,过程式代码难以添加新的数据结构,因为必现修改所有的函数,面向对象代码难以添加新函数,因为必现修改所有的类。
3、得墨忒耳律
模块不应该了解它所操作对象的内部情况。对象不应该通过存储器暴露其内部结构。
无论出于怎么样的初衷,公共访问器及改值器都把私有变量公开化,诱导外部函数以过程程序使用数据结构的方式使用这些变量。
此类混杂增加了添加新函数的难度,也增加了新数据结构的难度。
4、数据传送对象
最为精炼的数据结构,是一个只有公共变量,没有函数的类。
5、小结
对象暴露行为,影藏数据:便于添加新对象类型而无需修改既有行为,同时也难以在既有对象中添加新行为。
数据结构暴露数据,没有明显的行为:便于向既有数据结构添加新行为,同时也难以向既有函数添加新数据结构。
七、错误处理
错误处理很重要,但是如果它搞乱的代码逻辑,就是错误的做法。
1、使用异常而非返回码
2、先写Try-Catch-Final语句
执行Try-Catch-Final语句中try部分代码时,你是在表明可以随时取消执行,并在catch语句中接续。在某种 意义上,try代码就像是事务,catch代码块将程序维持在一种持续状态,
3、使用不可控异常
可控异常违背的开放/合并原则,如果你的方法中抛出可控异常,而catch语句在三个层级之上,你就得在catch语句和抛出异常之间的每个方法签名中申明这个异常。
4、给出异常发生的环境说明
应该创建信息充分的错误信息,并和异常一起传递出去。在消息中,包括失败的操作和失败类型,如果你的应用程序有日志系统,传递足够的信息给catch块,并记录下来。
5、依赖调用者需要定义异常类
对于代码的某个特定区域,单一异常类通常可行
使用定义异常类将第三个Api的异常打包为一个异常,好处在于你不必绑死某个特定厂商api设计,可以定义自己感觉舒服的API
6、定义常规流程
如果你遵循了前文提及的建议,在业务逻辑和错误处理代码之间就会有良好的区隔,大量的代码会变得像是简洁而简谱的算法。
如果有特殊逻辑,可以使用特例模式,创建一个类或者配置一个对象,用来处理特例。
7、别返回null值
返回null值基本上就是在给自己增加工作量,也是在给调用者添堵
8、别传null值
9、小结
整洁的代码是可读的,单也要强固,可读与强固并不冲突,如果将错误处理隔离开的,独立于主要逻辑之外,就能写出强固而整洁的代码。
八、边界
1、使用第三方代码
2、浏览和学习边界
3、学习性测试的好处不只是免费
无论如何我们都得学习要使用的API。而编写测试则是获取这些知识容易而不会影响其他工作的途径。
4、使用尚不存在的代码
编写想要得到的接口,好处之一是它在我们的控制执行,这有助于保持客户代码更可读,且集中于它该完成的工作。
5、整洁的边界
边界上会发生有趣的事情,改动是其中之一,有良好的软件设计,无需巨大投入和重写即可进行修改,在使用我们控制不了的代码时,必须加倍小心保护投资,确保未来的修改代价不至于太大。
边界上的代码需要清晰的分割和定义期望的测试。应该避免我们的代码过多的了解第三方代码中特定的信息。
九、单元测试
如果你坐视测试腐坏,那么代码也会跟着腐坏。
9.1 TDD三定律
定律1:在编写单元测试前,不可编写生产代码
定律2:只可编写刚好通过的单元测试,不能编译也算不通过
定律3:只可编写 刚好足以通过当前单元测试的生产代码
9.2 保持测试整洁
测试代码和生产代码一样重要
无论架构多有扩展性,无论设计划分的有多好,没有了测试,你就很难做改动,因为你担忧改动会引入不可预知的缺陷。
9.3整洁的测试
整洁的测试三要素:可读性,可读性,可读性,如何做到可读性,和其他代码一样:明确,简介,还有足够的表达力
9.4每个测试一个断言
单个断言是个好准则
更好的一些的规则或许是每个测试函数中只测试一个概率
我们不想要超长的测试函数,测试完这个又测试那个。
9.5 F.I.R.S.T
整洁的测试还遵循以下6条规则:快速、独立、可重复、自足验证、及时