March 27, 2022
By: kevin

代码评审和好的软件设计

  1. 鼓励好的代码设计
  2. 命名一致,遵循标准编程规范和项目既成规则。
  3. 鼓励更高级别的抽象和设计
  4. 积极处理异常情况
  5. 不可变性的表达和约束
  6. 鼓励帮助代码理解的注释
  7. 鼓励注释和自动化文档
  8. 鼓励使用语言的一些高级特性
  9. 鼓励编写测试代码
  10. 好好写log
  11. 代码规范化的好处

如果说写代码是程序员的工作的话, 写好代码就是善行了. 代码是是业(Karma), 佛家讲报应, 写过后马上就看不懂的代码会在后续的世代轮回中继续折腾我们.

毕竟

系统的代码存在的首要目的是让人读懂, 次要目的才是让机器执行. -- 唐纳德·克努特(Donald Ervin Knuth)

但阅读代码对人类而言,始终是个痛苦的过程, 我们记不住太多的东西, 处理不了太复杂的状态变化, 所以要分成模块、类、函数.

抽象是我们应对复杂的唯一方式, “好”代码也仅仅是让我们读起来不那么困难, 做到“可读性“ 已经是非常不易了.

代码评审的目的就是提高代码的一致性, 抽象性, 最终为了相对容易的读懂.

代码的几个级别, 1)可编译,2)可运行,3)可测试,4)可读,5)可维护,6)可重用。通过自动化测试的代码只能达到第3)级,而通过Code Review的代码少会在第4)级甚至更高。

接下来, 各位善人, 让我们一起看看如何改进一下我们的代码, 普度众生, 早脱苦海.

鼓励好的代码设计

降低视觉负担和复杂性.

  1. 小函数, 一般不要超过20行.
  2. 短行数,一行不要太长,不要超过80个字符长度.
  3. 函数尽量职责清晰单一.
  4. 函数参数尽可能少, 超过4个就要考虑封装为一个数据结构打包传递.
  5. 函数之间有空行.
  6. 该缩进的时候用缩进,使用空格缩进, 避免Tab.

命名一致,遵循标准编程规范和项目既成规则。

如果规范和项目现有规则冲突, 以项目既有规则为准.

  1. 项目中命名形式一致,遵循项目或者所选技术展的通用命名规范very_important_variable也好, VeryImportantVariable也罢
  2. 名词命名方式一致,因为我们用字母来写代码,在项目的始终,避免一词多义带来的混乱。

鼓励更高级别的抽象和设计

在代码中的if/else/case/cond这些条件分支, 如果同样的条件判断出现在多处, 就意味着抽象不足, 需要考虑使用多态性.

使用protocol, interface, multimethod等语言特性实现多态.

积极处理异常情况

  1. 在不可能发生场景加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 "未知动物";
       }
   }
  1. 如果异常要throw,明确异常的类型,不要笼统的Exception

不可变性的表达和约束

通用行为定义Interface.

同类型的不同行为实现应该定义为interface,代码中有interface代表了对抽象的思考.

通用的设计保持一致性.

成熟设计应该在项目中被反复使用.

鼓励帮助代码理解的注释

注释应该在代码之前,体现了思考和设计的过程.

注释应该和所注释的代码保持一致,物理上尽可能的接近.

注释着重描述“做了什么”,而不是“怎么做”,怎么做是代码干的事.

  1. 模块级别,比如Class的Package, C# 的Namespace, Clojure的Namespace
    1. 模块级别的注释应该在模块创建时写, 而不应该后补, 体现了设计过程
    2. 模块的功能描述
    3. 对外提供哪些接口
    4. 模块的设计思路(如果比较复杂)
  2. 设计单元: 函数、方法、枚举、struct、class, protocol, interface 等
  3. 对外函数写明:1. 作用 2. 参数 3. 特殊情况
  4. 如果函数无需返回或返回null,需做特殊说明

鼓励注释和自动化文档

Clojure的docstring, Java的JavaDoc都是可以通过特定工具生成文档的, 而且在各自的IDE都能够和自动提示关联.

所以特别要写的更具体清晰一些. 在clojure这种动态语言中. docstring显得特别重要, 传入参数的类型是不受编译器检查的. 传入的数据结构需要在docstring中描述下, 能放个例子是再好不过了.

鼓励使用语言的一些高级特性

鼓励面向对象语言中的函数式编程

  1. Java的Lambda & Stream
  2. C# 的Lambda, Linq

对于需要释放的资源的自动管理

  1. 意识到程序中有各类资源需要释放(文件, 数据库连结, socket, etc.).
  2. 可以使用语言提供的特性的, 比如, java的AutoClosable, Clojure的with-open, c#的using.
  3. 手动释放必须释放的资源.

鼓励编写测试代码

  1. 写代码的时候, 有测试代码代表了可测试性(代码独立, 没有外部依赖, 状态清晰).
  2. 代码测试是否能够自动执行.

好好写log

  1. 项目中使用日志库, 提供了灵活的输出目标, 可以定制等级, 线程安全, 不会乱掉, io高性能
  2. 避免使用各类print函数, 原因如上.

代码规范化的好处

  1. 规范的代码可以促进团队合作.
  2. 规范的代码可以减少bug的产生和处理.
  3. 规范的代码可以降低维护成本.
  4. 规范的代码有助于代码审查.
  5. 养成代码规范的习惯,有助于程序员自身的成长.
Tags: style