代码评审和好的软件设计
- 鼓励好的代码设计
- 命名一致,遵循标准编程规范和项目既成规则。
- 鼓励更高级别的抽象和设计
- 积极处理异常情况
- 不可变性的表达和约束
- 鼓励帮助代码理解的注释
- 鼓励注释和自动化文档
- 鼓励使用语言的一些高级特性
- 鼓励编写测试代码
- 好好写log
- 代码规范化的好处
如果说写代码是程序员的工作的话, 写好代码就是善行了. 代码是是业(Karma), 佛家讲报应, 写过后马上就看不懂的代码会在后续的世代轮回中继续折腾我们.
毕竟
系统的代码存在的首要目的是让人读懂, 次要目的才是让机器执行. -- 唐纳德·克努特(Donald Ervin Knuth)
但阅读代码对人类而言,始终是个痛苦的过程, 我们记不住太多的东西, 处理不了太复杂的状态变化, 所以要分成模块、类、函数.
抽象是我们应对复杂的唯一方式, “好”代码也仅仅是让我们读起来不那么困难, 做到“可读性“ 已经是非常不易了.
代码评审的目的就是提高代码的一致性, 抽象性, 最终为了相对容易的读懂.
代码的几个级别, 1)可编译,2)可运行,3)可测试,4)可读,5)可维护,6)可重用。通过自动化测试的代码只能达到第3)级,而通过Code Review的代码少会在第4)级甚至更高。
接下来, 各位善人, 让我们一起看看如何改进一下我们的代码, 普度众生, 早脱苦海.
鼓励好的代码设计
降低视觉负担和复杂性.
- 小函数, 一般不要超过20行.
- 短行数,一行不要太长,不要超过80个字符长度.
- 函数尽量职责清晰单一.
- 函数参数尽可能少, 超过4个就要考虑封装为一个数据结构打包传递.
- 函数之间有空行.
- 该缩进的时候用缩进,使用空格缩进, 避免Tab.
命名一致,遵循标准编程规范和项目既成规则。
如果规范和项目现有规则冲突, 以项目既有规则为准.
- 项目中命名形式一致,遵循项目或者所选技术展的通用命名规范very_important_variable也好, VeryImportantVariable也罢
- 名词命名方式一致,因为我们用字母来写代码,在项目的始终,避免一词多义带来的混乱。
鼓励更高级别的抽象和设计
在代码中的if/else/case/cond这些条件分支, 如果同样的条件判断出现在多处, 就意味着抽象不足, 需要考虑使用多态性.
使用protocol, interface, multimethod等语言特性实现多态.
积极处理异常情况
- 在不可能发生场景加assert, 第一时间发现异常情况
(defn get-anmials-name
"根据code找到动物"
[code]
(cond (= code "DOG")
"小狗狗"
(= code "CAT")
"小猫咪"
:else (assert false "不识别或还未匹配的动物")))
public static string GetAnimalName(string code)
{
switch (code)
{
case "DOG":
return "小狗狗";
case "CAT":
return "小猫咪";
default:
Debug.Assert(false, "不识别或还未匹配的动物");
return "未知动物";
}
}
- 如果异常要throw,明确异常的类型,不要笼统的Exception
不可变性的表达和约束
通用行为定义Interface.
同类型的不同行为实现应该定义为interface,代码中有interface代表了对抽象的思考.
通用的设计保持一致性.
成熟设计应该在项目中被反复使用.
鼓励帮助代码理解的注释
注释应该在代码之前,体现了思考和设计的过程.
注释应该和所注释的代码保持一致,物理上尽可能的接近.
注释着重描述“做了什么”,而不是“怎么做”,怎么做是代码干的事.
- 模块级别,比如Class的Package, C# 的Namespace, Clojure的Namespace
- 模块级别的注释应该在模块创建时写, 而不应该后补, 体现了设计过程
- 模块的功能描述
- 对外提供哪些接口
- 模块的设计思路(如果比较复杂)
- 设计单元: 函数、方法、枚举、struct、class, protocol, interface 等
- 对外函数写明:1. 作用 2. 参数 3. 特殊情况
- 如果函数无需返回或返回null,需做特殊说明
鼓励注释和自动化文档
Clojure的docstring, Java的JavaDoc都是可以通过特定工具生成文档的, 而且在各自的IDE都能够和自动提示关联.
所以特别要写的更具体清晰一些. 在clojure这种动态语言中. docstring显得特别重要, 传入参数的类型是不受编译器检查的. 传入的数据结构需要在docstring中描述下, 能放个例子是再好不过了.
鼓励使用语言的一些高级特性
鼓励面向对象语言中的函数式编程
- Java的Lambda & Stream
- C# 的Lambda, Linq
对于需要释放的资源的自动管理
- 意识到程序中有各类资源需要释放(文件, 数据库连结, socket, etc.).
- 可以使用语言提供的特性的, 比如, java的
AutoClosable, Clojure的with-open, c#的using. - 手动释放必须释放的资源.
鼓励编写测试代码
- 写代码的时候, 有测试代码代表了可测试性(代码独立, 没有外部依赖, 状态清晰).
- 代码测试是否能够自动执行.
好好写log
- 项目中使用日志库, 提供了灵活的输出目标, 可以定制等级, 线程安全, 不会乱掉, io高性能
- 避免使用各类
print函数, 原因如上.
代码规范化的好处
- 规范的代码可以促进团队合作.
- 规范的代码可以减少bug的产生和处理.
- 规范的代码可以降低维护成本.
- 规范的代码有助于代码审查.
- 养成代码规范的习惯,有助于程序员自身的成长.