正如书名所说,这本书主要讲述的就是如何写出真正整洁的代码。作者在本书中阐述了在命名、函数、注解、代码格式、对象和数据结构、错误处理、边界问题、单元测试、类、系统、并发编程等方面如何做到整洁的经验与最佳实践。属于代码编写方面的入门书籍,看完这本书,能让你对什么是整洁的代码以及如何编写整洁的代码有所初步的认识和了解,有利于你在以后的编程生涯中提高你的代码质量和尽可能的减少坏味道。
读完此书后,结合一些自己的编码经验做出了一些总结和回顾。希望对你有所帮助,如果喜欢的话还是建议去看原书。
整洁的代码
什么是整洁的代码
既然说到整洁之道,那么就要首先说明什么是整洁的代码,在书中是这样定义的。
整洁的代码应该:
- 能通过所有测试
- 没有重复代码
- 体现系统中的全部设计理念
- 包含尽量少的实体:如类、方法、函数。
这就是这本书对于整洁代码给出的定义,那么如何才能保证代码的整洁,换句话说我们应该怎么做才能让自己的代码变得整洁呢?
如何做到让代码整洁
答案其实很简单,那就是让你的代码每次签入时比签出时要更加的干净。这句话就好像如何在篮球场上赢得对手一样,只要得分比对方高即可。虽然说的很对,但是指导意义确实差了点。那么我们应该如何做呢?
首先如果你在一开始编码的时候就严格按照一些规则去进行编码,虽然能很好的保证代码的质量,但是容易缺少对整体的把握,也容易陷入到牛角尖,甚至将大量的时间浪费在一些作用不是很大的地方。造成了项目进度的延后。
正确的做法应该是:我们只需要首先构思好,然后再去打磨它。完全可以先去实现功能,保证功能的实现的基础上,再去分解函数、修改名称、消除重复、按照规则组装函数。
这样可以在保证功能完成的情况下来提高我们代码的整洁水平。
代码编写的规范
下面将从命名、函数、注解、单元测试、类、迭进、并发编程、坏味道、注解、函数等方面来讲一讲代码编写的规范。
有意义的命名
代码规范的第一个维度就是代码中的命名,大到项目的命名,小到类的命名、方法的命名和参数的命名都需要我们遵守一定的命名规范。
书中建议的命名的规范就是:
- 名副其实
就是让人一看到这个名字就知道是个什么东西,可以让人在阅读代码的时候能够立即明白这是干什么的,尽可能的减少阅读的障碍和减少理解的难度。
- 避免误导
在命名的时候,要尽量的准确,减少错误的误导。尤其是不要使用双关语,这样会让阅读你代码的人无所适从。另外要注意尽量不要适用小写l和大写O作为名称,因为有的时候真的很难区分它与1和0的不同。
- 做出有意义的区分
有的时候我们在命名后面添加数字来作为区分,但是这种区分除了能表示这两个不同以外,完全没有提供其他的正确信息,也没有提供导向作者意图的线索。这样的区分真的是很没有意义的区分。
- 使用读得出来的名称
如果我们的命名读不出来,或者比较拗口的话,在进行和其他人的讨论的时候就会显得傻乎乎的,另外往往还需要再对这个命名进行一番解释,这样的命名显然不是我们追求的。
- 使用可搜索的名字
这个规范是便于我们在我们的程序中去寻找我们的代码,在排查问题和代码的时候一个易于搜索的名字可以使我们很快的定位到代码所在的位置。这就是为什么名字的长度应与其作用域大小相对应。
- 类名对象名
类名和对象名应该是一个名词或者名词短语,不应该是一个动词。表明其是一个什么东西。
- 方法名
相反的,方法名应该是一个动词或者动词短语。表明其干了什么事情。
- 命名的一以贯之
也就是说给每个抽象概念选一个词,并且一以贯之。
以上就是如何让你的命名有意义的一些建议,当你下次写代码的时候不妨试试以上这些规则,看看代码的可读性是否有所提升。
函数
函数是代码的重要的组成部分,如果写的一团糟,虽然不影响功能的使用,但是容易将一些不起眼的bug隐藏起来,使得代码不健壮。而且可读性会很低。
所以对于函数来说我们要尽可能的抽离简单方法、重命名以及重构。这样就能让你的代码看起来舒适很多。
书中对于函数的规范就是:
- 短小
函数规范的首要任务就是短小,没有人有足够的耐心去看一个有着几百行代码的函数,除非迫不得已。在很多规范中就提到一个函数最多不能超过八十行。这样才能使得函数易于阅读和理解。
- 只做一件事情
函数应该做一件事情。做好这件事情。只做这一件事情。判断的标准也很简单,就是看是否能再拆出一个函数来。
- 每个函数一个抽象层级
就是函数中的语句都要在同一层级上。函数中混杂不同的抽象层级,往往会让人迷惑。使得无法判断某个表达式是基础概念还是细节。
一个优美的代码应该让我们在阅读代码的时候拥有自顶向下的阅读顺序。这样的代码读起来逻辑十分的清晰和完整。
- 使用描述性的名称
这一条在命名中提到过,函数要尽可能的使用动词+关键字这种格式来进行命名。这样可以让阅读者不用看这个函数的具体代码就知道这个函数做了什么事情。能够更好的在这个层次去整体的把握这个函数。
- 函数的参数
函数的参数应该尽量的少,不要多于三个,如果实在多于三个,那就需要考虑进行封装和抽象了。
- 分割指令与询问
通俗的来说就是函数要么做什么事情,要么就回答什么事情,两者不可兼得。要不状态判断的时候会导致混乱。
- 抽离try/catch代码
往往来说try/catch代码会使得我们的函数看起来十分的糟糕。搞乱了代码的结构,使得把错误处理和正常流程混为一谈。最好的措施就是把这块代码从主体中分离出来形成函数。
- 错误处理就是一件事情
处理错误的函数就应该专心致志的去处理错误,不该再去处理其他的事情。
- 别重复自己
重复的代码会导致代码臃肿。而且重复可能是软件中一切邪恶的根源。很多原则和实践规则都是为了控制与消除重复而创建的。所以在我们的代码中也要尽可能的去减少代码的重复。这样在后续代码修改的时候也只需要修改一处。这样能减少修改代码的风险。
上面就是我们在函数的书写中需要遵守或者注意的规范。但是如何才能写出这样的函数来呢?
如何写出尽可能完美的函数
写代码和写其他东西一样,都是可以先想什么就写什么,然后再去打磨它。初稿也许无序,但是只要能够实现功能,后续就进行斟酌推敲直到达到完美函数的目的。
一些很实用的方法就是:分解函数、修改名称、消除重复、缩短方法、重新安置方法、甚至拆散类。最后再遵循上述规则组装好我们的函数。
注解
注解?天使与魔鬼!
因为什么都比不上良好的注解来的有用。什么也不会比糟糕的注释更有本事搞乱一个模块。什么也不会比陈旧、错误信息的注释更有破坏性。
但是注解永远是个辅助作用,最关键还是代码的本身,带有少量注释的简洁代码,比带有大量注释的琐碎复杂代码要好的多。所以每当我们像写注释的时候,先去思考我们的代码有没有优化的空间,如果能通过优化代码(重新命名)来达到目的的话,那就不要去写注释。
而且注释永远不是代码,注释也许会撒谎,但是代码从来不会。注释会随着代码的变动、演化而失去一些原本的意义和作用,如果不能及时维护的话。
好注解
上面说了这么多,注释的不好,那么一个好的注释应该是什么样子的呢?
- 提供信息的注释、对意图的解释
注解的常规用处,就是用来提供一些代码的基本信息和代码的意图,不过这种注释往往会被更好的命名来取代。
- 阐述
通常用来阐述参数以及返回值的含义。
- 做出警示
警告其他程序员会出现某种后果,为啥要这么写。
- TODO
我们一些代办的事情,用来提醒。
- javadoc
当我们在编写公共API的时候,就要为它提供良好的Javadoc。
坏注释
- 喃喃自语
- 多余的注释
- 误导性注释
- 日志式注释
- 能用函数或变量解决的不要使用注释
- 尾注释
当然坏的注释还有很多,这里就不过去的阐述,只要记住,只有当我们迫不得已的时候才会使用注解。
单元测试
在功能的开发中,功能的实现和测试代码同样重要。没有测试代码的话就会失去保证生产代码可扩展性的一切要素。正是单元测试让你的代码可扩展、可维护、可复用。有了单元测试以后,就不会担心对代码的修改。
而整洁的测试最主要的要素就是可读性。如何做到可读性呢?其实就是:明确、简洁、还有足够的表达能力。书中对于整洁的测试有着以下的五条规则,即FIRST:
- 快速:测试代码应该能够快速运行,因为我们需要频繁运行它。
- 独立:测试应该相互独立,某个测试不应该依赖上一个测试的结果,测试可以以任何顺序进行。
- 可重复:测试应可以在任何环境中通过
- 自足验证:测试应该有bool值输出,不应通过查看日志来确认测试结果,不应手工对比两个文本文件确认测试结果。
- 及时:及时编写测试代码。单元测试应该在生产代码之前编写,否则生产代码会变得难以测试。
类
一个整洁规范的类应该具有的特点:
- 短小
- 单一权责原则:类或模块应有且只有一条加以修改的理由。
- 高内聚
- 隔离修改
迭进
通过迭进设计达到整洁目的。其设计的规则:
- 运行所有测试
- 不可重复
- 表达了程序员的意图
- 尽可能减少类和方法的数量
并发编程
并发编程要比单线程编程要困难的多,所以这一块我们将讨论并发编程的需求及其困难支持,并结合实际给出一些对付这些难点、编写整洁的并发代码的建议。
为什么要并发
既然并发编程比单线程编程要困难的多,那么我们为什么要引入并发编程呢?
在以往的单线程应用中,目的与时机紧密耦合。这样就导致了系统的吞吐量并不高。所以为了提高系统的吞吐量我们要想办法将目的与时机进行解耦。这样就能使得程序并行的处理请求。
但是并发编程也不是万能的,往往会陷入一些诸如:并发总能改进性能,编写并发程序无序修改设计等误区,对于并发编程相对中肯的态度应该是:
- 并发会在性能和编写额外代码上增加一些开销
- 正确的并发是复杂的,即使对于简单的问题。
- 并发的缺陷并非总能复现,所以有些时候当成偶发事件,从而忽略
- 并发常常需要对设计策略进行根本性的修改。
并发防御原则
并发问题通常比较棘手,所以我们在进行编码的时候要注意防御并发带来的问题。由此本书给出了一些防御并发问题的原则和一些小技巧:
-
单一权责原则(SRP):方法、类、组件应当只有一个修改的理由。
-
- 严格限制数据作用域
- 使用数据复本
- 线程应该尽可能地独立
-
了解Java库
-
- 使用类库提供的线程安全的类
- 使用executor框架执行无关任务
- 尽可能的使用非锁定解决方案
-
了解执行模型
-
- 生产者消费者模型
- 读写模型
- 哲学家模型
-
警惕同步方法之间的依赖
避免使用一个共性对象的多个方法。
- 保持同步区域微小
- 将伪失败看做可能的线程问题
代码一旦涉及到了并发就会使得代码很难编写正常。尤其是在加入都现成和共享数据以后,简单的代码也会变成噩梦。要编写并发代码,就要严格地编写整洁的代码,否则将面临微细和不频繁发生的失败。
味道与启发
注解
- 不恰当的信息
- 废弃的注解
- 冗余注解
- 糟糕的注解
- 注释掉的代码
函数
-
过多的参数
-
- 封装成DTO类
-
输出参数
-
标识参数
-
死函数
-
函数只做一件事情
-
长函数
-
- 进行函数拆分,拆分成若干个小函数
一般性问题
-
不正确的边界行为
-
忽视安全问题
-
重复代码
-
- 抽象成公共函数
-
死代码
-
人为耦合
-
遵循标准约定
-
魔法值
-
- 使用常量代替
-
过大的类
-
- 按照单一职责进行拆分
最后
- 如果觉得看完有收获,希望能关注一下,顺便给我点个赞,这将会是我更新的最大动力,感谢各位的支持
- 欢迎各位关注我的公众号【java冢狐】,专注于java和计算机基础知识,保证让你看完有所收获,不信你打我
- 求一键三连:点赞、转发、在看。
- 如果看完有不同的意见或者建议,欢迎多多评论一起交流。感谢各位的支持以及厚爱。
——我是冢狐,和你一样热爱编程。