浅谈编程中的命名(短文)

一篇短文谈下我对编程中「起名字」这个事的看法和感悟。

一句话总结:用词要达意、保持一致性 和 整体的简洁性

展开来说有 7 点:

  1. 用词讲究表意准确、言简意赅

    对程序描述的事物理解越深刻,命名就越凝练、越精准。

    一个例子是 fork 函数,意为 “分叉”,既简洁又准确 (fork man page)。

    再比如,冒泡排序 BubbleSort、堆的上浮 Swim 和 下沉 Sink 过程的命名都很形象。

    命名要「名副其实」,不可诱人误解。 比如 C++ 中的 std::exchange 函数, 看名字似有交换之意,实际并不然 💔。

  2. 名字风格应符合社区惯例、项目约定

    几乎每个编程语言都有官方的、或社区流行的命名风格约定, 例如:Python PEP8Effective GoGoogle C++ Style Guide 等。

    用何种风格有时众口难调,但是 风格统一比风格本身的美丑更重要

  3. 约定俗成的名字更胜一筹

    计算机是有历史的,就会有其独特的用词意境。比如,迭代变量 ij, 交换函数通常叫 swap, 哈希 hash (why?) 等。

    在一个编程语言内,也有其约定俗称的用词。比如,对于 Go 语言,经常用 b 表示 Buffer, r 表示 Reader, w 表示 Writer 等。

    保持命名的一致性很重要,同一事物尽量用相似的名字

    比如,在一个项目内,如果大多代码都用 conn 来表示 Connection 实例,新代码就要避免另造新词。 如果你有多个连接实例要起名字,可以用 conn1, conn2 或者 conn_sever1, conn_server2 之类的名字来区分, 这样它们仍然是相似的。

    简写的短名字有时更直观,比如常用的 max, min, pow, abs, eof 等。 具体项目中,可以对高频概念发明一些短名字, 比如用 msg 来表示消息 Message,用 o 来表示订单 Order, 用 w 来表示仓库 Warehouse, 标品 standard_product 可以叫 sp 等。放到项目所处的业务环境中看,这些短名字并不会晦涩难懂, 并进一步成为「约定俗成」。

    在表意清晰的情况下,我更偏好短的名字。 比如说, acquiretake_ownership 更好。

  4. 对象的用途不同、名字形式不同

    我总结了几点,并非绝对,权供参考:

    • 函数用动词,形如 Do() 或者 DoSomething()

      func Write([]byte) int {}
      func MakeOrder(req MakeOrderRequest) MakeOrderResponse {}
      func ReadFrom(r Reader) (n int, e error){}
      
    • 类、对象、变量、结构体等用名词, 且区分单复数:

      type Buffer struct {}
      buf := &Buffer{}  // 单数
      parts := strings.Split(",") // 复数
      
    • 布尔值和判断函数,命名以 is/has/should/contains/check 等开头 (is 最常用):

      var isExist bool
      func IsUserLogin() bool {}
      func HasPrefix() bool {}
      

      此条并不绝对,只要用词有「是否」之意即可,比如 found

      func Contains(s, part string) (found bool)
      
    • 只做数据增删改查的函数,考虑用 Get/Set/Query/List/Update/Delete 等字眼:

      GetValue()     // 查询单个
      SetValue()     // 设置单个
      QueryValues()  // 查询多个值
      ListValues()   // 列出多个值
      UpdateValue()  // 更新单个值
      DeleteValue()  // 删除单个值
      
    • 注意动词的时态:

      Status.Moving    // 移动中
      Status.Paid      // 已支付
      order.CreatedAt  // 下单时间
      
  5. 不必要在名字中携带类型信息

    大多数情况,不必要透出对象的类型信息,因为上下文环境,阅读代码的人很容易知道其类型:

      // bad
      counter_integer := 0
      content_string := "abcdefg"
      Encode(dst_bytes, src_bytes []byte)
      Trim(str string, prefix_str string)
    
      // better
      counter := 0
      content := "abcdefg"
      Encode(dst, src []byte)
      Trim(s, prefix string)
    

    相比而言,名字中的业务和功能信息,更为重要

  6. 简洁的好名字留给高频场景

    举例说,现在有两种映射表,一种是有序的、一种是无序的。现在有三个名字: ordered_map, unordered_mapmap。如何从中选出两个名字 ?

    就这个问题而言,无序表的使用场景更多,所以我偏向于这样起名字:

    • ordered_map 给有序表
    • map 给无序表

    有趣的是,在 C++ 中,可能由于历史原因, 命名方式是完全与此相反的, map 是基于红黑树的有序表, unordered_map 是无序的哈希表, 但是显然无序表更常用 💔。

  7. 关注整体的简洁性

    局部变量、尤其临时变量、高频使用的变量,尽量采用简短的名字,会有助于整体的简洁性

    其实就是,排版会更整洁,更容易整体阅读。

    对比下面的两对代码片段,你更喜欢左边的、还是右边的呢?

    我是比较喜欢右边,整体排版更简洁。

    Golang 是推崇短名字的,一些链接:

关于命名,就谈到这里,欢迎评论区交流。

(完)


相关阅读: 浅谈编程中的打日志(短文)

本文原始链接地址: https://writings.sh/post/naming

王超 ·
喜欢这篇文章吗?
微信扫码赞赏
评论 首页 | 归档 | 算法 | 订阅