如何更好的组织Go程序代码|Go主题月

235 阅读4分钟

Go代码的组织方式与其他语言不同。本篇文章讨论了如何为你的Go程序的元素命名和打包,以最好地服务于用户。

选择好名字

您选择的名称会影响您对代码的理解,所以在命名您的包及其导出的标识符时要小心。

一个包的名称为其内容提供了上下文。例如,标准库中的字节包导出的是Buffer类型。单独来看,Buffer这个名字并不具有很强的描述性,但是当与它的包名结合在一起时,它的含义就变得很清楚了:bytes.Buffer。如果包有一个描述性不强的名字,比如util,那么缓冲区很可能会获得一个更长更笨拙的名字util.BytesBuffer。

不要羞于在工作中重命名。随着时间的推移,你会更好地理解程序的各个部分是如何结合在一起的,因此,它们的名字应该是什么。没有必要把自己锁定在早期的决定中。(gofmt命令有一个-r标志,它提供了一个语法感知的搜索和替换,使大规模的重构变得更容易。)

一个好的名字是软件界面最重要的部分:名字是每一个代码的客户端首先看到的东西。因此,一个好的名字是好文档的起点。下面的许多做法都是由好的命名有机地产生的。

选择一个好的导入路径(让你的软件包可以 "去获取")。 导入路径是用户导入软件包时使用的字符串。它指定了包的源代码所在的目录(相对于GOROOT/src/pkgGOROOT/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个文件中。

GO外文翻译计划blog.golang.org/organizing-…