思维导图

1. 指导原则

1.1. 清晰 - Clarity

Programs must be written for people to read, and only incidentally for machines to execute.*

程序必须是为人们阅读而编写的,只是偶尔为机器执行而已

1.2. 简单 - Simplicity

简单是可靠性的先决条件,控制复杂性是计算机编程的本质

构建软件设计有两种方法:

一种方法是使它简单到明显没有缺陷

另一种方法是使它复杂到没有明显缺陷,比第一种方法要困难得多

1.3. 生产力 - Productivity

设计是今天安排代码工作的艺术,而且永远是可变的

你花了多少时间来做有用的工作、等待工具、或者无可救药地迷失在外部代码库中。

  • 有个笑话说,Go 是在等待 c + + 程序编译时设计的。快速编译是围棋的一个关键特性,这有助于 Go 开发人员感觉自己与使用动态语言工作的同行一样高效,而不会遇到这些语言固有的可靠性问题。

  • Go 程序员意识到编写代码是为了被读取,因此将读取代码置于编写代码之上。因此,甚至通过工具和自定义强制所有代码按照特定的样式进行格式化,这样他们不会花时间去理解他们的同事写了什么。,根本目的就是提供生产力。

注:性能和并发性是重要的属性,但是没有清晰性、简单性和生产力重要。

2. 标识符

2.1. 选择标识符是为了清晰,而不是简洁

好的命名就像一个好笑话,如果你必须解释它,那就不好笑了

Obvious code is important. What you can do in one line you should do in three.

显而易见的代码比炫技更重要

好名字的特性

  • 一个好的名字是简洁的,但不一定是最短的。

  • 好的名字是描述性的。

    一个好的名字应该描述变量或常量的应用,而不是它们的内容。

    一个好的名字应该描述一个函数的结果,或者一个方法的行为,而不是它们的实现。

    一个好的名字应该描述一个包的用途,而不是它的内容。名称的选择越好,它就能更准确地描述它所识别的事物。

  • 一个好的名字应该是可以预测的。您应该能够仅从一个符号的名称就推断出它的使用方式。

2.2. 标识符长度

The greater the distance between a name’s declaration and its uses, the longer the name should be.*

一个名称的声明和它的使用之间的距离越大,名称就应该越长

由此我们可以得出一些指导方针:

  • 当变量的声明和最后一次使用之间的距离很短时,简短的变量名可以很好地工作。
  • 长变量名称需要自我调整; 长变量名称需要提供的值越多。冗长的名称在页面上的比重应该是很低的。
  • 不要在变量名中包含类型的名称。
  • 常量应该描述它们所拥有的值,而不是如何使用这个值。
  • 使用单个字母变量表示循环和分支,使用单个字符表示参数和返回值,使用多个字符表示函数和包级声明
  • 对于方法、接口和包,更倾向于使用简单的单词。
  • 请记住,包的名称是调用者用来引用它的名称的一部分,因此要利用它。

2.2.1. 语境是关键

重要的是要认识到大多数关于命名的建议都是上下文相关的。我喜欢说它是一个指导方针,而不是一个规则。

I 和 index 这两个标识符之间的区别是什么。例如,我们不能下结论说一个比另一个好

1
2
3
for index := 0; index < len(s); index++ {
//
}

基本上更易读

1
2
3
for i := 0; i < len(s); i++ {
//
}

2.3. 一个变量的名字应该描述它的内容

你不应该把你的变量命名为他们的类型,就像你不把你的宠物命名为“狗”和“猫”一样。出于同样的原因,不应该将类型的名称包含在变量名称的名称中。

变量的名称应该描述其内容,而不是其内容的类型:

1
2
3
4
var (
companiesMap map[string]*Company
productsMap map[string]*Products
)

显然 Map 后缀并不能提高代码的清晰度,去掉后缀后,我们只剩下更简洁和更具描述性的内容:

1
2
3
4
5
var (
users map[string]*User
companies map[string]*Company
products map[string]*Products
)

建议应避免使用类似于变量类型的后缀,这个建议也适用于函数参数。

歧义举例:usersMap[一个用Map类型存储user集合] userMap[表示一个用户地图的业务实体概念],如果我们从名称中去掉表示类型类型的后缀,usersMap 就变成了users 变成一个描述性词汇 ,反观 userMap 就变成了user 就会产生误导性。

2.4 使用一致的命名风格

一个好名字应该是可预测的。当读者第一次遇到一个名字时,他们应该能够理解它的用法。当他们遇到一个共同的名字,他们应该能够假设它没有改变的含义,因为他们最后一次看到它。你也可以说一个好的名字应该让人感到熟悉。

  • 某些单字母变量通常与循环和计数相关联。例如,i、 j 和 k 通常是简单 For 循环的循环感应变量。
  • N 通常与计数器或累加器相关联。
  • V 是泛型编码函数中值的常用简写,k 通常用于表示映射的键。
  • a 和 b 是比较同一类型的两个变量的参数的通用名称。
  • x 和 y 是为比较而创建的局部变量的通用名称,s 通常用作 string 类型的参数的简写。

2.5 使用一致的声明风格

Go 至少有六种声明变量,设计者认识到这可能是一个错误,但现在改变它为时已晚,他们认为,更大的问题是遮蔽。

变量不同的声明格式

1
2
3
4
5
6
7
8
9
10
11
x := 1

var y = 2

var z int = 3

var a int; a = 4

var b = int(5)

c := int(6)

注:因为Go中没有从一种类型到另一种类型的自动转换; 赋值操作符左侧的类型必须与右侧的类型相同。编译器可以从右边的类型推断出要声明的变量的类型。

下面👇是一些建议,说明如何使读者清楚地了解每个声明的目的。

  • 在声明而不是初始化变量时,使用 var。
1
2
3
4
5
var players int    // 0  不推荐 var players = 0

var things []Thing // an empty slice of Things

var thing Thing // empty Thing struct 不推荐 var things = nil

解释:var 充当一个线索,表明这个变量已经被故意声明为指定类型的零值。这也符合在包级别使用 var 声明变量的要求,而不是使用短声明语法。

  • 在声明和初始化时,使用 : =

当同时声明和初始化变量时,建议使用短变量声明形式,这样不会让变量被隐式初始化为零值,令读者清楚地看到赋值过程。

1
2
things := make([]Thing, 0)
thing := &Thing{} // 显式地使用指向 Thing {}的指针初始化 不推荐: thing := new(Thing)
  • 其他

当然,任何经验法则都有例外。例如,有时两个变量是密切相关的,所以写作

1
2
var min int
max := 1000

但下面的方式可能更易读

1
min, max := 0, 1000

2.6 团队精神

保持一致性,即使它不是你的首选方法,从长远来看,比你的个人偏好更有价值。

软件工程的目标,即生成可维护的代码。因此,你可能会把你的大部分职业生涯花费在那些你不是唯一作者的项目上。要有团队精神,在这种情况下,我的建议是: 跟随当地的风格

小结

  • 软件工程的目标,即生成可维护的代码;
  • 选择标识符是为了清晰,而不是简短 ;
  • 变量名长度和它的使用距离有关;
  • 变量名字应该具有描述性、可预测性;
  • 变量的命名与声明风格应统一 ;

参考:

Practical Go: Real world advice for writing maintainable Go programs