Go 语言代码风格规范-指南篇

发表于 1年以前  | 总阅读数:290 次

每门开发语言都会有其特有的风格规范(亦或指南),开发者遵循规范能带来显著收益,有效促进团队协作、减少 bug 错误、降低维护成本等。

Google 开源的 Google Style Guides (https://google.github.io/styleguide/)为多种编程语言提供了风格规范,包括 C++、Java、Python、JavaScript 等。在 2022 年 11 月,Go 语言风格规范(https://google.github.io/styleguide/go/index)也终于得到开源。

Google 发布的 Go 语言风格规范一共包括四部分内容。

  • 概述篇:https://google.github.io/styleguide/go/index
  • 指南篇:https://google.github.io/styleguide/go/guide
  • 决策篇:https://google.github.io/styleguide/go/decisions
  • 最佳实践篇:https://google.github.io/styleguide/go/best-practices

风格原则

这里列举了一些总体原则,它们总结了思考如何编写可读性 Go 代码。以下是根据重要性排序的可读性代码特性。

  • 清晰性:对读者来说,代码的目的和基本原理是清楚的。
  • 简单性:代码以尽可能简单的方式实现其目标。
  • 简洁性:代码具有高信噪比。
  • 可维护性:编写的代码易于维护。
  • 一致性:代码与更广泛的 Google 代码库风格一致。

1. 清晰性

可读性的核心目标是编写对读者而言清晰的代码。

清晰主要是通过有效的命名、有用的注释和高效的代码组织来实现的。

清晰性应该是从读者的角度来看,而非代码编写者。代码易于阅读比易于编写更重要。代码清晰性有两个不同的方面:

  • 代码实际上在做什么?
  • 为什么代码会做它所做的事情?
代码实际上在做什么?

Go 的设计使得它应该相对直接地看到代码在做什么。在存在不确定或者读者需要先验知识才能理解代码的情况下,这值得花时间让未来的读者更清晰地了解代码的用途。例如,以下措施可能会有助于提供更清晰的代码:

  • 使用更具描述性的变量名
  • 添加附加注释
  • 用空格和注释分解代码
  • 将代码重构为单独的函数/方法,使其更加模块化

这里没有放之四海而皆准的方法,但在开发 Go 代码时优先考虑清晰性很重要。

为什么代码会做它所做的事情?

代码的基本原理通常通过变量、函数、方法或包的名称充分传达。如果没有,添加注释很重要。当代码包含了读者可能不熟悉的细微差别时,解释”为什么“就显得尤为重要。例如:

  • 语言上的细微差别,例如,闭包要获取一个循环变量,但是代码写在很多行之外。
  • 业务逻辑的细微差别,例如,需要区分实际用户和冒充用户的访问控制检查。

某个 API 可能需要额外注意才能正确使用。例如,出于性能原因,一段代码可能错综复杂且难以理解,或者一系列复杂的数学运算可能以意想不到的方式使用类型转换。在这些场景以及更多类似的情况下,重要的是要有随附的注释和文档来解释它们,这样以后的维护者才不会犯错误,这些读者能够不用进行逆向工程而理解代码。

同样重要的是要注意到,一些为了提供清晰性的尝试(例如添加额外的注释)可能会模糊代码的实际目的,例如增加混乱、重复描述代码、与代码自相矛盾的注释,或者为了保证注释最新而增加维护负担。让代码自己说话(例如,通过使用可自描述的符号名称)而不是添加多余的注释。注释通常最好是解释为什么做某事,而不是代码在做什么。

Google 代码库在很大程度上是统一与一致的。通常情况下,与众不同的代码(例如,通过使用不熟悉的模式)那样做是有充分理由的,通常是为了性能考虑。保持这一特性很重要,它可以让读者清楚地知道在阅读一段新代码时他们应该把注意力集中在什么地方。。

标准库包含许多实践该原则的示例。例如其中:

  • sort 包中的维护者注释。
  • 同样在 sort 包中有一些很好且可运行的例子,它们同时有利于使用者(例子在[godoc](sort package - sort - Go Packages)显示)和维护者(作为部分测试用途的例子)。
  • strings.Cut 虽然只有四行代码,但它们提高了对调用者而言,使用时的清晰性和正确性。

2. 简单性

对于那些使用、阅读和维护它的人来说,你的 Go 代码应该是简单的。

Go 代码应该以实现其目标的最简单方式编写,无论是在行为还是性能方面。在 Google Go 代码库中,简单的代码意味着:

  • 易于从上到下阅读
  • 不假设你已经提前知道它在做什么
  • 不假设你可以记住前面的所有代码
  • 没有不必要的抽象层次
  • 没有引起人们对普通事物的注意的名字
  • 使读者清楚值和决策的传播情况
  • 有注释解释为什么而不是什么,以及代码正在做什么以避免以后的偏差
  • 有独立的文档
  • 有有用的错误和有用的失败测试用例
  • 可能经常与“故作聪明”的代码相互排斥

在代码简单性和 API 使用简单性之间可能会出现权衡。例如,让代码更复杂可能是值得的,这样 API 的终端用户可以更容易且正确地调用 API。相比之下,为 API 的终端用户留一些额外的工作也可能是值得的,这样代码仍然简单易懂。

当代码需要复杂性时,应该刻意地增加复杂性。如果需要额外的性能,或者一个特定的库或服务有多个不同的用户,这通常是必要的。复杂性应该是合理的,但它应该随附文档,以便用户和未来的维护者能够理解和驾驭复杂度。这应该辅以证明其正确用法的测试和示例,尤其是在同时存在“简单”和“复杂”代码使用方式的情况下。

我们力求避免代码库中不必要的复杂性,以便当复杂性确实出现时,它表明这些相关代码需要仔细理解和维护。理想情况下,应该附有注释,它解释基本原理并确定应注意的事项。在优化代码以提高性能时经常会出现这种情况;这样做通常需要更复杂的方法,比如预分配缓冲区并在 goroutine 的整个生命周期中重用它。当维护者看到它时,这应该是一个线索,表明相关代码是性能的关键代码,未来对该段代码进行修改时应该持有谨慎态度。另一方面,如果使用不当,这种复杂性会给那些将来需要阅读或更改代码的人带来负担。

如果代码的目的很简单但最终实现却很复杂,这通常是应该重新查看实现以确定是否有更简单的方式来完成相同目的的信号。

最少机制

如果有多种方式可以表达相同的想法,请选择使用最标准的一种。复杂的机制常在,但不应无故使用。根据需要而增加代码的复杂性很容易,而在发现不必要的复杂性后,消除现有的复杂性要困难得多。

  1. 在足以满足你的用例时,使用核心语言结构(例如 slice、map、循环或 struct)。
  2. 如果没有,请在标准库中寻找一种工具(如 HTTP 客户端或模板引擎)。
  3. 最后,在引入新的依赖项或自己造轮子之前,请考虑 Google 代码库中是否有对应的核心库。

例如,考虑包含绑定到具有默认值的变量的标志的生产环境代码,该默认值必须在测试中被覆盖。除非打算测试程序的命令行界面本身(例如,使用 os/exec),否则直接覆盖绑定值比使用 flag.Set 更简单,也更可取。类似地,如果一段代码需要对集合成员进行检查,那么一个布尔值类型的 map(map[string]bool)通常就足够了。仅当需要更复杂的操作而 map 无法实现或使用起来过于复杂,才应使用提供类集合类型和功能的库。

3. 简洁性

简洁的 Go 代码具有很高的信噪比。应该很容易地辨别相关细节,它通过命名和代码结构引导读者去详细了解。

在任何时候,都会有很多东西阻碍代码呈现最主要的细节:

  • 重复代码
  • 外来语法
  • 晦涩难懂的名字
  • 不必要的抽象
  • 空格

重复的代码模糊了每个几乎相同部分之间的差异,它需要读者通过视觉比较相似的代码行已找到变化的地方。表格驱动测试室一个很好的机制示例,它可以从每次重复的重要细节中简明地提取出通用代码,但是选择将哪些部分包含在表格中将影响表格的易懂程度。

在考虑多种方式组织代码时,需要考虑哪种方式能让重要的细节最明显。

理解和使用常见的代码结构和正宗用法对于保持高信噪比也很重要。例如下面的代码块在错误处理中很常见,读者可以很快理解这块的用途。

// Good:
if err := doSomething(); err != nil {
    // ...
}

如果代码看起来与此非常相似但略有不同,读者可能不会注意到变化。在这种情况下,值得通过添加注释以引起注意,来有意“增强”错误检查的信号。

// Good:
if err := doSomething(); err == nil { // if NO error
    // ...
}

4. 可维护性

代码被编辑的次数比它编写的次数多得多。具有可读性的代码不仅对试图理解其工作原理的读者有意义,而且对需要更改它的程序员也有意义。清晰性是关键。

  • 可维护性的代码:
  • 易于被未来的程序员正确地修改
  • 具有结构化的 API,以便它们可以优雅地增长
  • 清楚代码所做的假设,它选择对应到问题结构的抽象,而不是对应到代码结构
  • 避免不必要的耦合,不包含未使用的功能
  • 有一个全面的测试套件,以确保承诺的行为得到维护,重要的逻辑是正确的,并且测试用例失败时能提供清晰、可操作的诊断。

当使用像接口和类型这样的抽象时,根据定义从它们使用的上下文中删除信息,重要的是要确保它们提供了足够的好处。编辑器和 IDE 可以直接连接到方法定义并在使用具体类型时显示相应的文档,但在其他情况下只能参考对应的接口定义。接口是一个强大的工具,但使用它也有代价,因为维护者可能需要了解底层实现的细节才能正确使用接口,这必须在接口文档或调用处进行解释。

可维护的代码也避免了将重要的细节隐藏在容易被忽视的地方。例如,在以下的代码例子中,一个字符的存在都会对理解代码产生重大的影响。

// Bad:
// The use of = instead of := can change this line completely.
if user, err = db.UserByID(userID); err != nil {
    // ...
}
// Bad:
// The ! in the middle of this line is very easy to miss.
leap := (year%4 == 0) && (!(year%100 == 0) || (year%400 == 0))

这些都并非不正确,但都可以以更明确的方式编写,或者可以附带注释以引起对重要行为的注意。

// Good:
u, err := db.UserByID(userID)
if err != nil {
    return fmt.Errorf("invalid origin user: %s", err)
}
user = u
// Good:
// Gregorian leap years aren't just year%4 == 0.
// See https://en.wikipedia.org/wiki/Leap_year#Algorithm.
var (
    leap4   = year%4 == 0
    leap100 = year%100 == 0
    leap400 = year%400 == 0
)
leap := leap4 && (!leap100 || leap400)

同样的,一个隐藏了关键逻辑或者重要边缘情况的辅助函数会很容易地使得之后的更改中可能没有合适地考虑到它。

可预测的命名是可维护代码的另一个特征。包的用户或一段代码的维护者应该能够预测给定上下文中的变量、方法或函数的名称。相同概念的函数参数和接收者通常应该共享相同的名称,这既可以使文档易于理解,也可以以最小的开销促进代码重构。

可维护代码应最小化其依赖性(隐式和显式)。依赖更少的包意味着可以影响行为的代码行更少。避免对内部或者没有文档记录的行为的依赖,在将来这些行为发生改变时,这些代码就不太可能会成为维护负担。

在考虑如何组织或编写代码时,值得花时间去思考代码可能随时间的推移而演进的方式。如果给定的方法更有利于未来更简单、更安全的更改,那通常是一个很好的权衡,即使这意味着设计稍微复杂一些。

5. 一致性

一致的代码是在更广泛的代码库中、在团队或包的上下文中,甚至在同一个文件中,看起来、感觉和行为都类似的代码。

一致性问题不会凌驾于上述任何原则,但如果必须打破平衡,那么打破平衡以保持一致性通常是有益的。

包内的一致性通常是最直接重要的一致性级别。如果同一个问题在包中以多种方式处理,或者如果相同概念在一个文件中有多个名称,则可能会非常不协调。但是,即使这样也不应该凌驾于文档化的风格原则或全局一致性之上。

核心指南

这些指南收集了所有 Go 代码都应遵循的 Go 风格最重要的方面。我们希望在编写可读性代码的时候学习并遵循这些准则。这些准则预计不会被经常更改,并且新添加的内容必须被高标准审核。

下面的指南扩展了 Effective Go 中的建议,这些建议为整个社区的 Go 代码提供了一个共同的基线。

格式化

所有 Go 源文件必须符合 gofmt 工具输出的格式。此格式由 Google 代码库中的提交前检查强制执行。生成的代码通常也应该格式化(例如,通过使用 <span style="font-size: 15px;">format.Source),因为它也可以在代码搜索中浏览。

驼峰命名

Go 源代码在编写多词名称时使用 MixedCaps 或mixedCaps (驼峰命名) 而不是下划线 (蛇式)。

即使它打破了其他语言的约定,这也适用。例如,常量如果是可导出的,则为 MaxLength(而非 MAX_LENGTH),如果为未导出的,则为 maxLength(而非 max_length)。

为了首字母大写为可导出的目的,局部变量被视为未导出的。

代码行长度

Go 源代码没有固定的代码行长度。如果一行感觉太长,应该考虑重构而不是折断。如果它已经尽可能短,那么应该允许该代码行保持变长。

不要分割代码行的情况:

  • 在缩进更改之前(例如,函数声明、条件)
  • 使某个长字符串(例如 URL)适合多个较短的行
命名

命名与其说是科学,不如说是一门艺术。在 Go 中,名称往往比许多其他语言短一些,但适用相同的一般准则。命名时应该注意:

  • 使用时不会感到重复
  • 考虑上下文
  • 不再重复已经清楚的概念

你可以在【决策篇】中找到更多关于命名的具体指导。

局部一致性

在风格指南没有提及特定风格点的地方,作者可以自由选择他们喜欢的风格,除非代码非常接近的地方(通常在同一个文件或包中,但有时在团队或项目目录中) 在该问题上采取了一致的风格。

有效的局部风格考量的例子:

  • 使用 %s 或 %v 格式化打印错误
  • 使用缓存 channel 代替互斥锁

无效的局部风格考量的例子:

  • 代码行长度限制
  • 使用基于断言的测试库

如果局部风格与风格指南不一致,但可读性影响仅限于一个文件,它通常会在代码审查中浮出水面,一致的修复将超出相关 CL(change list,变更清单) 的范围。那时,提交一个 bug 以追踪该修复是合适的。

如果一个更改会让现有的风格偏差恶化,在更多的 API 层面被暴露出来,扩大存在偏差的文件数量,或引入了一个实际的 bug,那么对于新代码而言,局部一致性不再是违反风格指南的有效理由。在这些情况下,作者应该在同一个 CL 下清理现有的代码库,在当前 CL 之前进行代码重构,或者找到一个至少不会使得局部问题变得更糟的替代方案。

本文由哈喽比特于1年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/rnYT3GKax_F32jAf0h-eqg

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:7月以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:7月以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:7月以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:7月以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:7月以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:7月以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:8月以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:7月以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:7月以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:7月以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:7月以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:7月以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:7月以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:7月以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:7月以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:8月以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:8月以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:8月以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:7月以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:7月以前  |  398次阅读  |  详细内容 »
 相关文章
Android插件化方案 5年以前  |  236879次阅读
vscode超好用的代码书签插件Bookmarks 1年以前  |  6915次阅读
 目录