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
命令可以自动获取和安装包。
如果您不使用托管的源代码库,请选择一些唯一的前缀,例如域名、公司或项目名称。例如,所有 Google 内部 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
博客索引