你好,我是猿java。
作为一名 Java程序员,如果把代码比作孩子,代码质量就如同孩子的健康,因此,作为技术人员,日常工作势必坚守两条红线:
- 保证代码交付质量
- 保证代码交付时间
对于这两条红线,其实不难理解,总结成一句话:在规定的时间内交付高质量的代码。
那么,什么是高质量的代码?如何才能写出高质量的代码?为什么有的程序员工作 5年,写出来的代码质量还不如 3年的程序员?今天我们就来聊一聊。
一、什么是高质量代码
代码的“好”与“坏”是一个相对的描述,因此,高质量代码也是一个很宽泛的概念,很难给它下一个精准的定义,但是,我们可以结合日常的开发工作经验,给出几个常见的衡量维度:
1. 可读性
高质量的代码首先需要具备可读性,我们编写的代码除了需要让机器能编译运行之外,更需要让程序员读懂,因为只有程序员读懂了它,才能更好地修 bug,添加新功能,做后期维护等等。
可能有小伙伴会说,Spring的源码很难读懂,因此它不具备可读性,这种理解是错误的,Spring框架是 Java生态中比较优秀的开源代码,读不懂是因为“内功”不够,本文的可读性是指代码命名是否规范,注释是否合理,分层是否清晰以及是否具备高内聚低耦合等特性。
2. 可维护性
现实工作中,很难遇见一次性代码(开发部署完之后再也不需要迭代),大部分情况都需要在一个模块上不断地迭代新功能和新代码,因此,高质量的代码必须具备可维护性。
可维护性反馈到代码上,可以通俗地表达成:bug能修改,老代码改得动,新功能可添加。而这些更改花费的代价就体现了可维护的难和易,比如:改动是否会增加大量的新 bug,改动对现有的逻辑的破坏性有多大,或者说改动的时间是否会很长。
3. 可扩展性
在代码设计 SOLID 中有一个很重要的原则就是开闭原则,要求代码需要 “对修改关闭,对扩展开放”,因此,高质量的代码需要具备可扩展性。在应对业务迭代时,开发者需要多关注代码能否用最小的改动来适配新的功能。
4. 可复用性
在日常开发中,尽量不重复造轮子,具体到代码上,不应该出现大量重复的代码,代码应该保持简洁,重复的代码可以抽离出来,实现代码复用。因此,可复用性也是衡量高质量代码的一个重要标准。
5. 可测试性
单测是开发人员保证代码质量的一个重要方法,因此,编写的代码是否具备可测试性,也是衡量代码高质量的一个标准。如果编写的代码很难写单元测试,那是否意味着代码设计存在很大的问题?
在分析了高质量代码的几个常用衡量维度之后,我们可以总结:所谓高质量的代码,其实就是衡量能否写出可读性好,易维护,易扩展,复用性高,可测试的代码。
接下来提供十种高效提升代码质量的方法。
二、提升代码质量方法
1. 规范命名
众所周知,一个好的名字可以使人受用一生,对于代码也是如此,好的命名可以帮助读者更好地理解代码的功能。
对于计算机科班出身的小伙伴,或许在大学的第一节编程课就被强调:代码命名要简明扼要并且见名知意,不要使用拼音,不要使用非公认的缩写等等,下面为代码命名的几点建议:
(1) 使用常见的英文单词,不使用拼音
代码的命名,应该尽量使用常见的英文单词,这样对于大部分人来说能够知道其意图。不要使用拼音命名,在软件开发领域,英文是主要的编程语言和命名规范。使用拼音给代码命名违反了行业的惯例和标准而且可读性差。
(2) 使用公认的缩写,否则使用全称
有时候,为了简化命名,会使用缩写,但是这些缩写一定要是业内大家公认的,比如:Impl 指代 Implement, str 指代 String,num 指代 number,del 指代 delete。
(3) 使用和团队一致的命名风格
现在的开发大部分是团队合作,因此,代码命名应该在团队内部保持一致,比如:和数据库交互的 Reporitory层,一般都是 CRUD方法的封装,如果团队使用 selectXXX 代表查询,我们就不要使用getXXX 或者 queryXXX,这样检索查询的方法时,可以根据select定位,同理,对于添加数据是使用 addXXX 还是 saveXXX 或者 insertXXX,也需要保持一种方式。
(4) 命名不要太长
命名要尽量的简短,而且能准确的传达意思,如果需要使用长命名,建议不要超过5个单词。
温馨建议:如果你在日常工作中对于命名拿不准时,可以给身边的同事参考,看看他们对该命名是否和你的本意是一致,这样也能帮助你更好的去命名。
2. 巧用注释
命名可以帮助读者从字面上了解代码的功能,但无法传达代码更多细节,因此,需要借助注释来完成这部分功能。喜欢查看源码的小伙伴应该可以发现,在 JDK,Spring等这些优秀的开源框架的源码中,类,方法甚至变量,几乎都有相应的注释,而我们在使用某个功能时,最直接的方式往往是通过官方提供的注释来了解其功能,因此,注释的重要性可想而知。
因此,注释是命名的补充和增强。
注释需要写什么内容呢?这里以 java.util.Collections 为例, 如下图:
通过 JDK 的 Collections源码注释可以看出:注释的内容大概包含 5部分信息:what, why, how, time, authors 即是什么?为什么?怎样做?时间 和 作者。我们可以根据需要灵活的搭配来添加注释。需要注意的,注释也需要简明扼要,尽量使用一些提炼,解释和总结性的语句,切勿长篇大论。
3. 代码风格
一个好的代码风格可以让代码看起来很清爽,降低代码阅读的复杂度。代码风格主要体现在以下几点:左括号风格,代码缩进,空行,超长行,魔数,方法代码过多,方法参数过多
- 左括号风格在代码行尾还是换行使用左括号,这个根据个人习惯,不过团队合作要求保持一种风格。
- 代码缩进代码编写时,为了美观,一般都不会顶格,需要缩进,因此常见的缩进有 两格缩进和四格缩进,选择哪一种也是根据个人习惯,不过团队合作要求保持一种风格。
- 空行在方法内部,我们可以使用空行,将逻辑独立的代码块区分,这样,一方面可以更方便的阅读方法实现,另一方面,当独立的代码块代码量增多时,可以考虑将代码块抽离成新的方法。
- 超长行每行的代码量不要太长,通常建议电脑一屏的宽度为准,如果超过一屏,建议换行。
- 魔数在代码编写时,不要出现魔数,我们要善于使用常量去定义这些魔数,增加代码的可读性。
- 方法代码过多每个方法中的代码量不要太多,因为代码太多会增加方法阅读的复杂度,通常建议电脑一屏的高度为准,超过了就要考虑能否再抽离出新方法。
- 方法参数过多编写方法的时候,通常都会定义入参,一般建议入参的个数不要超过5个,如果再多就需要考虑封装对象来传递参数。
以上代码风格如果是团队合作,最好是保持一致。更多代码规范 推荐《代码整洁之道》和《阿里巴巴开发手册》
4. 快速短路
如下的伪代码,当 user == null时,可以通过 return快速短路返回,去除多余的 esle 语句。因此,在日常业务开发中,遇到类似的情况,一定要做到快速短路,切莫使用大量嵌套。比如 if-esle 语句可以使用 return 快速短路;对于 for 循环可以使用 break,continue,return等提前跳出,减少不必要的遍历;
public User getUser(String id) {
// get by id
User user = getById(id);
if(user == null){
return null;
} else {
// else 逻辑
}
// 优化成
if(user == null){
return null;
}
// else 逻辑
5. SOLID原则
在类或者方法的设计上要尽量遵守 SOLID原则,SOLID原则是业内一个比较的原则,关于 SOLID原则可以参考小编以往的文章:优雅代码,从 SOLID 开始
6. 设计模式
设计模式在代码中的使用是一个比较高阶的能力体现,巧用设计模式,可以大大提高代码的可维护性、可扩展性和可重用性等功能,在很多开源的框架上都有设计模式的体现,比如:Spring 中的单例模式(Single Pattern)工厂模式(Factory Pattern),代理模式(Proxy Pattern),适配器模式(Adapter Pattern),模板模式(Template Pattern),观察者模式(Observer Pattern)等等。
如果你对设计模式不是很熟悉,推荐书籍 Erich Gamma 的《设计模式》,另外,JDK 和 Spring框架也是一个很好的学习资料,可以对照着源码中设计模式的代码,加强对设计模式的理解,然后可以尝试着在日常开发中去使用某些设计模式。
7. 单元测试
单元测试是开发人员保证代码质量最直接的方式,但现实工作中却被很多人忽略,有的借故业务开发忙,没有把时间写单测,因此把测试功能全部丢给测试人员,有的是不如何写单测,关于单元测试,可以参阅小编以往文章:TDD,什么是 测试驱动开发?
8. 数据结构
有人说 程序 = 数据结构 + 算法,足以看出数据结构和算法的重要性,作为 Java程序员,我们经常使用的 HashMap, List, Set, String 等都是 Java 语言对数据结构的一种友好封装,关于数据结构,可以参阅小编以往文章:数据库,你必须掌握的8种数据结构! 数据结构之美:为什么 MySQL 选择 B+树做索引?
在实际工作中,很多人面对的工作绝大多数是业务开发,所以处理数据肯定离不开数据结构,建议平时多研究 JDK 的源码,比如:Map,List 等常用的数据结构的底层实现原理,这样在使用时才能得心应手应手。
9. 算法
近些年,算法在技术面试中的比重越来越大,特别是在一些知名的互联网公司面试,如果代码中能够合理地使用算法,将事半功倍。需要说明的是,在日常的业务开发中,手写算法的概率比较低,一般会使用三方框架,比如:Google Guava中的限流算法。
对于算法,似乎没有很好的捷径,小编的建议是:经常去算法网站上刷题,一方面让算法能力处于随时可以面试的状态,一方面通过大量的算法实现量变到质变,掌握算法的精髓。
10. 重构
重构是代码迭代中一直会存在的行为,当发现代码有 Bad Smell “坏味道”时,得考虑代码是否需要重构。重构,一般分为小重构和大重构,小重构通常是指工作量小,时间可控(比如时间不超过 2个工作日),甚至不需要测试参与。大重构是指时间周期比较大,对代码的改造比较大,一般需要整体设计,然后分阶段进行。关于代码重构,可以参考微服务作者 Martinfowler 的博客:https://martinfowler.com/books/refactoring.html 或者 Martinfowler 的书籍:《重构,改善既有代码的设计》第二版,《重构与模式》《修改代码的艺术》
三、总结
本文分析了高质量代码的几个衡量维度:
- 可读性
- 可维护性
- 可扩展性
- 可复用性
- 可测试性
在编写代码时,应该多关注上面几个因素,一开始可以刻意练习,然后慢慢变成一种习惯,这样编写的代码质量应该不会太差。
接着我们介绍了提升代码质量的 10种常用方法:
- 规范命名
- 巧用注释
- 代码风格
- 快速短路
- 单元测试
- SOLID原则
- 设计模式
- 数据结构
- 算法
- 重构
前 4种方法更多体现在代码的“形”上,体现了程序员对代码细节的把握,而后 6种方法则更多体现了代码的“神”,体现了程序员基本功和能力。
根据小编多年的工作经验,前 4种方法是最简单,提升代码质量最见效的方法,它们几乎和能力和工作年限无关,完全在于程序员能否扣住细节,如果能够在这 4个方法上多花点功夫,编写的代码已经可以甩很多人一条街了。
对于后 6种方法,是开发人员内功的体现,它要求的是一个长期的积累和修炼过程,是开发人员能力差距的最好体现,所以,通过这 6点可以很好地定位某个程序员的编程段位。建议平时多阅读一些业内大牛的经典书籍。
四、个人心得
工作中,经常会遇见一些工作 5年的程序员,写的代码质量却远不如 3年的程序员,为什么?小编觉得有以下几个原因:
1. 职业规划不清晰
对于他们来说,大部分都没有清晰职业规划,没有方向自然就没有努力的目标,没有目标就很容进入日复一日循环重复工作的僵局。
2. 代码能用就行
工作中小编也和一些人讨论过,他们对代码的态度是:没有必要这么苛刻,能用就行。如果是刚进入社会工作,或许还能理解,如果已经工作 3年以上,还是这种想法,竞争力在哪里?作为程序员,代码质量不就是最好的武器吗?
3. 编程不是兴趣
对于很多人来说,选择编程并不是因为喜欢而是觉得它的工资相对其他行业来说会高一些。众所周知,兴趣是最好的老师,如果长年累月的干一件没有兴趣的事,能干好吗?
4. 外部诱惑太多
游戏,短视频等外部诱惑太多,一玩停不下来,工作之余的大部分时间都被这些“精神鸦片”消耗殆尽。
因为篇幅有限,不可能把每个细节点都讲清楚,分享一个小编多年来一直在践行的学习方法:持续学习,多听,多看,catch 他人优秀的 idea,然后不断地丰富和领会该 idea,最终形成自己的方法论。丰富和领会的过程就在不断地强大自己,相信长年累月的积累,终有一天你也可以写出让人赏心悦目的高质量代码。