架构师技能3:如何做code review

语言: CN / TW / HK

目前该文只是简单理论的总结,后续优化增加例子来说明。

前言

CodeReview的目的是提升代码质量,尽早发现潜在缺陷与BUG,降低修复成本,同时促进团队内部知识共享,帮助更多人更好地理解系统。

如何做好code review,可以参考谷歌 code review 指南。原文地址: https://google.github.io/eng-practices/review/reviewe

code review从大的方面来说:

1、业务层面:业务逻辑是否正确等相关内容。例如

1) 数据库 字段的设计是否合理。

2) 业务流程 是否按照详细设计的流程走。

3) 业务扩展性, 设计是否影响后续业务的拓展。

2、代码层面:是否符合优秀特质。 优秀代码--改善代码三部曲 https://guisu.blog.csdn.net/article/details/7658644

没有“完美”的代码,只有更好的代码。由于每个团队的code review的方法论或者流程不一样,本文单纯从reviewer的角度来总结如审查提交的代码是否符合优秀的代码特质。

优秀代码的特质

能做好code review,前提是reviewer理解什么是更好的代码。这是需要一定具备很强的编程能力,不仅需要熟练运用相关设计,还具备丰富编码实战经验。

好代码就像 玩笑无需解释。

优秀代码的特质

•内聚Cohesive:内聚的代码更容易理解和查找 bug

松耦合Loosely Coupled: 松耦合的代码让实体之间的副作 用更少,更容易测试、复用、扩展

•封装Encapsulated:封装良好的代码有助于管理复杂度,  也更容易修改

•自主 Assertive:自主的代码其行为和其所依赖的数据放在 一起,不与其它代码互相干预(Tell but not Ask)

• 无重复Nonredundant:无冗余的代码意味着可以只在一个  地方修复bug和进行更改。

内聚:一个模块内各个元素彼此结合的紧密程度

耦合:一个软件结构内不同模块之间互连程度的度量。

松耦合:系统的模块与模块之间,尽可能的使其独立存在,也就是单一职责。

模块与模块之间的接口,尽量的少而简单。

高耦合:模块相互之间依赖性比较多,模块与模块之间的关系也比较复杂。导致的问题是一个模块出现了问题,其他功能模块都不能正常运行,然后定位问题也比较困难。

系统如何“高内聚、低耦合”?将程序积木化。

内聚性越强,则要求的函数越多(每个函数只作一件“事”),这样,将它们组合成“大”的功能,也就越复杂,就不可能达到松耦合。因此,应在二者之间作出平衡与折衷的选择,这也体现程序员的水平。

应该从系统论的角度来看:

首先系统是有层次的,即系统可以分为子系统。

其次模块化:模块可分为子模块。

“强内聚、松耦合”的“度”的把握,应结合系统的次层性来考虑,即通常应在层次性上作出折衷,如:模块内子程序(下一个层次上)应共享数据(有一定的耦合度),而减少全局变量能降低子程序性间的耦合性。

面向对象的语言进一步强化了“强内聚、松耦合”,类的封装性既强调了相关内容(数据及其操作)的内聚,又强调了类的独立性和私密性。

比如类之间的关系,继承就组合的耦合性强,组合是类与类之间通常通过接口的契约实现服务提供者/服务请求者模式,这就是典型的松耦合。

3、优秀代码层面的 code review 主要内容:

一、设计层面

1、功能性设计:

是否符合功能性设计:如果存在代码和设计有出入的地方需要问询为什么要变动,因为这些变动有可能是出于开发者在真正设计代码时候的深入考虑,或者是由于一时大意出现偏差。

2、设计重构

1)、抽象原则: 两个方法有相似逻辑时,应该抽取相似逻辑,处理不同逻辑,而不是将相似的逻辑再写一遍。

2)、模块化原则: 模块划分确保是模块内具有高内聚度、模块间具有低耦合度,最终程序积木化。CR就要审查类的边界是否合理以及类和类直接的耦合程度。

3)、封装原则: 隐藏对象的属性和实现细节,仅对外公开接口,并且控制访问级别。code review主要审查成员变量的可见性,哪些方法应该是public,哪些方法应该是private。方法封装是否彻底。

4)、层次结构原则: 代码分层,让不同层次的代码做不同的动作。层次清晰的代码,提高可读性,从代码结构就大概能了解到代码是如何分层,每层大概功能是什么。例如常用的Controller、Service、Mapper/Dao三层代码结构,其各层的代码逻辑范围。CR的时候检查代码逻辑放在哪层是否比较合理,是否符合团队工程规范。 java工程规范和浅析领域模型VO、DTO、DO、PO

3、设计原则

设计模式原则详解很早之前的博文:《设计模式原则详解》

一)、解藕原则: 解决耦合性问题,尽少依赖外部。

1、单一职责:CR就要检查类的职责过多。

2、开闭原则:CR审查抽象类/接口是否合理。

3、迪米特法则:在类的划分上、结构设计上、以及对其他类的引用是否合理。

二)、接口原则

4、依赖倒置:依赖接口编程。

5、接口隔离:接口分类和专职。

三)、继承父子原则: 既有解藕又有接口。

6、里氏替换原则:CR审查继承关系违反了里氏代换原则(尽量不要重写和重载父类的方法)。同时建议在程序尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象 。

7、合成/聚合原则。建议要尽量使用对象组合,而不是继承关系达到软件复用的目的

二、实现层面

1、一致性

比如命名的一致性问题:

private UserRepo userDao;

2、复杂性

圈复杂度过高的方法应该分类抽离进行处理,适当的场景可以用多态代替分支。

1)源文件不能有重复代码块

2)控制流 "if", "for", "while", "switch", "try" 等不能嵌套太深

3) 类之间不耦合或轻耦合

4)类不能有太多方法

5)类不能有太多域

6)不能使用魔数

7)"switch" 不能有太多的 "case"

3、命名

编码规范只是规范命名规则,如使用驼峰命名法还是其他命名法;而好的命名风格应该是看到变量或者函数名字就能“望文生义”,毕竟我们不能把自己写的所有代码都做注释。     Java工程规范和浅析领域模型VO、DTO、DO、PO

4、可测性:

1)、函数不能太复杂。

2)、函数不能有太多参数。

3)、表达式不能太复杂。

5、性能、并发、安全检查等等。

三、规范层面

1、代码规范

团队的编码规范。

2、文档和注释

1)、团队要求完善的文档。

2)、无用的逻辑要及时清理, 并且要清理干净,   避免造成干扰。

3)、是否注释的代码。