Go 博客
组织 Go 代码
引言
Go 代码的组织方式与其他语言不同。本文讨论如何为您的 Go 程序元素命名和打包,以使其更好地服务用户。
选择好的名称
您选择的名称会影响您对代码的思考方式,因此在命名软件包及其导出的标识符时要小心。
软件包的名称为其内容提供了上下文。例如,标准库中的 bytes 软件包导出了 Buffer
类型。单独来看,名称 Buffer
并不是很具描述性,但与软件包名称结合后,其含义就变得清晰了:bytes.Buffer
。如果软件包的名称描述性较差,例如 util
,那么缓冲区可能会获得更长、更笨拙的名称 util.BytesBuffer
。
在工作时不要羞于重命名事物。随着您花时间在程序上,您将更好地理解其各个部分如何配合,从而更好地确定它们的名称。没必要被早期的决定束缚住。(gofmt 命令有一个 -r
标志,提供了语法感知的搜索和替换功能,使大规模重构更容易。)
好的名称是软件接口最重要的部分:名称是代码的每个客户端首先会看到的东西。因此,精心选择的名称是良好文档的起点。许多以下实践都是从好的命名自然而然产生的。
选择好的导入路径(让您的软件包可以“go get”)
导入路径是用户导入软件包时使用的字符串。它指定了软件包源代码所在的目录(相对于 $GOROOT/src/pkg
或 $GOPATH/src
)。
导入路径应该是全局唯一的,因此请使用您的源代码仓库路径作为其基础。例如,来自 go.net
子仓库的 websocket
软件包的导入路径是 "golang.org/x/net/websocket"
。Go 项目拥有路径 "github.com/golang"
,因此该路径不能被其他作者用于不同的软件包。因为仓库 URL 和导入路径是相同的,go get
命令可以自动获取和安装软件包。
如果您不使用托管的源代码仓库,请选择一些唯一的、例如域名、公司或项目名称作为前缀。例如,谷歌所有内部 Go 代码的导入路径都以字符串 "google"
开头。
导入路径的最后一个元素通常与软件包名称相同。例如,导入路径 "net/http"
包含 http
软件包。这不是强制要求 - 如果您愿意,可以设置不同的名称 - 但为了可预测性,您应该遵循此约定:用户可能会惊讶于导入 "foo/bar"
会将标识符 quux
引入软件包命名空间。
有时人们会将 GOPATH
设置为他们的源代码仓库根目录,并将软件包放在相对于仓库根目录的子目录中,例如 "src/my/package"
。一方面,这使得导入路径变短("my/package"
而不是 "github.com/me/project/my/package"
),但另一方面,这会破坏 go get
功能,并迫使用户重新设置 GOPATH
才能使用该软件包。请不要这样做。
最小化导出的接口
您的代码可能由许多有用的代码片段组成,因此很容易将大部分功能暴露在软件包的导出接口中。抵制这种冲动!
您提供的接口越大,您需要支持的就越多。用户很快就会依赖您导出的每个类型、函数、变量和常量,从而形成一个隐含的契约,您必须永久遵守它,否则可能会破坏用户的程序。在准备 Go 1 时,我们仔细审查了标准库的导出接口,并删除了我们尚未准备好承诺的部分。在分发您自己的库时,您也应该采取类似的谨慎态度。
如果拿不准,就不要导出!
一个软件包中应该包含什么
很容易将所有东西都扔进一个“大杂烩”软件包中,但这会稀释软件包名称的含义(因为它必须包含大量功能),并迫使只使用软件包一小部分的用户编译和链接大量不相关的代码。
另一方面,也很容易过度地将代码拆分成小软件包,在这种情况下,您很可能会陷入接口设计的困境,而不是把工作做完。
请参考 Go 标准库作为指导。它的一些软件包很大,一些很小。例如,http 软件包包含 17 个 Go 源文件(不包括测试)并导出 109 个标识符,而 hash 软件包由一个文件组成,只导出三个声明。没有硬性规定;这两种方法在其上下文中都是合适的。
话虽如此,main
软件包通常比其他软件包更大。复杂的命令包含大量在可执行文件上下文之外几乎没有用处的代码,通常将所有代码保存在一个地方更简单。例如,go 工具超过 12000 行,分布在 34 个文件中。
为您的代码编写文档
良好的文档是可用和可维护代码的基本品质。阅读 Godoc:为 Go 代码编写文档 一文,了解如何编写好的文档注释。
下一篇文章:App Engine 1.7.1 中的 Go 更新
上一篇文章:GCC 4.7.1 中的 Gccgo
博客索引