Golang - Organizing Go Code

简述

讨论如何命名和打包Go程序的元素,以便提供最佳服务

1. 选择一个好的名字

名称会影响对代码的看法

1.1. 包的名称提供其内容的上下文

名称Buffer不是很具描述性,但当与bytes包结合时候,bytes.Buffer含义变得清晰

如果包具有较少描述性的名称,例如util,缓冲区应该获得更长且更笨拙的名称util.BytesBuffer以方便理解。

1.2. 在工作时不要羞于重命名

在go使用过程中,好的命名方便更好地理解各个部分是如何组合在一起的。

同时,也注意,没有必要将自己锁定在早期决策中,(gofmt命令有一个-r标志,提供语法感知搜索和替换,使大规模重构更容易。)

1.3. 一个好名字是软件Interface最重要的部分

名称是代码的每个客户端将看到的第一件事。因此,精心挑选的名称是良好文档的起点。许多实践都是通过良好的命名来有机地产生的。

2. 选择一个好的import path(方便"go get”)

导入路径是用户导入包的字符串,它指定包源代码所在的目录:相对于$GOROOT/src/pkg$GOPATH/src

2.1. 导入路径应该是全局唯一的

应该使用源存储库的路径作为其基础

比如"golang.org/x/net/websocket”,由于存储库URL和导入路径是同一个,因此该go get命令可以自动获取并安装包。

如果不使用托管源存储库,请选择一些唯一的前缀,例如域,公司或项目名称。例如,所有Google内部Go代码的导入路径都以字符串开头"google”。

2.2. 入路径的最后一个元素通常与包名称相同

例如,导入路径"net/http"包含包http。这不是一个要求 - 如果你愿意,你可以使它们不同 - 但你应该遵循惯例的可预测性:不要让用户惊讶,导入"foo/bar"将标识符quux引入包名称空间。

2.3. 不要把GOPATH为源存储库的根

有时人们设置GOPATH为源存储库的根,并将其包放在相对于存储库根目录的目录中,例如设置GOPATH="src/my/package"

一方面,虽然这会使导入路径保持较短("my/package"而不是"github.com/me/project/my/package"),但另一方面,它会中断go get并强制用户重新设置其GOPATH使用包。

不要这样做!!

3. 最小化的接口导出

抵制导出接口中公开大部分功能的冲动

代码可能由许多有用的代码组成,因此很有可能在程序包的导出接口中公开大部分功能。抵制那种冲动!

提供的Interface导出度越大,必须支持的越多。

用户将很快依赖于您导出的每种类型,功能,变量和常量,相当于创建一个隐含的合同,您必须永久尊重或冒险破坏用户的程序。

在准备Go1时,Go开发者也仔细检查了标准库的导出接口,并删除了他们尚未准备好承诺

4. 包中应该放的内容 - KISS原则

在开发过程中,很容易将所有内容都放入包中,但这会淡化包名的含义(因为它必须包含很多功能)并强制包的一小部分用户编译和链接很多不相关的代码。

另一方面,将代码拆分成小包也很容易,在这种情况下,您可能会陷入Interface的设计中,而不仅仅是完成工作。

查看Go标准库作为指南。标准库的一些包很大,有些很小。例如,http包包含17个源文件(不包括测试)和导出109个标识符;同时哈希包由一个仅导出3个声明的文件组成,没有硬性规定; 鉴于其背景,这两种方法都是适当的

按照这样说,main包通常大于其他包。复杂的命令包含许多执行外的上下文代码,通常只需将它们保存在一个地方。(例如,go工具在34个文件中分布了超过12000行。)

5. 做好代码的文档

良好的文档是可用和可维护代码的基本质量。 Go项目认真对待文档。文档是使软件易于访问和维护的重要组成部分。当然它必须写得很好并且准确,但它也必须易于编写和维护。理想情况下,它应该与代码本身耦合,以便文档随代码一起发展。程序员创建好文档越容易,对每个人来说都越好。

Godoc文档撰写

5.1. godoc文档工具

Godoc解析Go源代码(包括注释), 并以HTML或纯文本形式生成文档。最终结果是文档与它所记录的代码紧密结合。

例如,通过godoc的Web界面,您只需单击一下即可从函数文档导航到其实现。

5.2. godoc约定

约定很简单:记录类型,变量,常量,函数甚至包,在其声明之前直接写一个常规注释,没有插入空行。然后,Godoc将该注释作为文本与其记录的项目一起呈现。

5.2.1. 函数注释

注释是一个完整的句子,以它描述的元素的名称开头,比如Fprint

当工具为了简洁而截断它时,例如当它们提取第一行或句子时,它会更好地读取。

// Fprint formats using the default formats for its operands and writes to w.
// Spaces are added between operands when neither is a string.
// It returns the number of bytes written and any write error encountered.
func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
//...
}

5.2.2. 包注释

关于包声明的注释应提供包文档大致描述。这些评论可能很短,就像sort包的简要说明一样:

它们也可以像gob包的概述一样详细。该软件包对需要大量介绍性文档的软件包使用另一种约定:软件包注释放在其自己的文件doc.go中,该文件仅包含那些注释和package子句。

// Package sort provides primitives for sorting slices and user-defined
// collections.
package sort

5.2.3. doc.go - 独立的包注释

软件包对需要大量介绍性文档的软件包使用另一种约定:软件包注释放在其自己的文件doc.go中,该文件仅包含那些注释和包子句:

gob包的doc.go:

/*
Package gob manages streams of gobs - binary values exchanged between an
Encoder (transmitter) and a Decoder (receiver). A typical use is transporting
arguments and results of remote procedure calls (RPCs) such as those provided by
package "net/rpc".

The implementation compiles a custom codec for each data type in the stream and
is most efficient when a single Encoder is used to transmit a stream of values,
amortizing the cost of compilation.
....
....
See "Gobs of data" for a design discussion of the gob wire format:
https://blog.golang.org/gobs-of-data
*/
package gob

在编写任何大小的包注释时,请记住他们的第一句话将出现在godoc的包列表中。

5.2.4. BUG注释 - 不会被godoc忽略

从godoc的输出中省略了与顶级声明不相邻的注释,但有一个值得注意的例外:

“BUG(who)”开头的顶级注释被识别为已知错误,并包含在软件包文档的“Bugs”部分中。 “who”部分应该是可以提供更多信息的人的用户名。例如,这是bytes包中的已知问题:

// BUG(r): The rule Title uses for word boundaries does not handle Unicode punctuation properly. 

5.3. 兼容性注释问题

有时结构字段,函数,类型甚至整个包都变得多余或不必要,但必须保持与现有程序的兼容性。

要表示不应使用标识符,请在其文档注释中添加一个段落,该段落以“已弃用:”开头,后跟一些有关弃用的信息。标准库中有一些示例。

5.4. godoc转HTML格式规则

  1. 随后的文本行被视为同一段的一部分; 你必须留一个空行来分隔段落
  2. 预格式化文本必须相对于周围的注释文本缩进(类似markdown的语法,参见gob/doc)
  3. 网址将转换为HTML链接; 不需要特殊标记。

5.5. godoc小结

关于godoc最小方法的最好的事情是使用它是多么容易。因此,许多Go代码(包括所有标准库)都遵循这些约定。

您自己的代码只需通过上述注释即可提供良好的文档。内部安装的任何Go包$GOROOT/src/pkg和任何GOPATH工作空间都可以通过godoc的命令行和HTTP接口访问,您可以通过-path标志或仅"godoc ."在源目录中运行来指定索引的其他路径。

有关更多详细信息,请参阅godoc文档。