如果你在问自己,你的 Go 包应该有多大,是把相关的类型放在同一个包里,还是把它们分成不同的包,可以从关于包名的 Go 博文开始。尽管帖子的标题是这样的,但它并不仅仅是关于命名的。它包含了一些有用的提示并引用了一些有用的文章和讲座。
这里有一些其他的考虑和说明。
用户在一页中看到包的godoc,任何由包提供的类型导出的方法都按其类型分组。Godoc 还将构造函数与它们返回的类型一起分组。如果客户端代码可能需要两个不同类型的值来相互作用,那么把它们放在同一个包里对用户来说可能很方便。
包内的代码可以访问包内未导出的标识符。如果你有几个相关的类型,它们的实现是紧密耦合的,把它们放在同一个包里可以让你实现这种耦合,而无需用这些细节污染公共 API。
综上所述,把你的整个项目放在一个包里可能会使这个包变得太大。当一个东西在概念上是不同的,给它一个自己的小包可以使它更容易使用。客户端所知道的包的短名称和导出的类型名称一起构成了一个有意义的标识符:例如bytes.Buffer
, ring.New
。
Go 风格在文件大小方面很灵活,因为维护者可以将包内的代码从一个文件移到另一个文件而不影响调用者。但作为一般准则:通常情况下,一个文件有几千行,或者有许多小文件,都不是一个好主意。没有像其他一些语言那样的 “一个类型,一个文件 “的约定。作为一个经验法则,文件应该足够集中,以便维护者可以知道哪个文件包含了什么东西,而且文件应该足够小,以便一旦有了这些东西,就很容易找到。标准库经常将大型包分割成几个源文件,将相关的代码按文件分组。bytes
包 的源代码就是一个很好的例子。有很长包文档的包可以选择专门的一个文件,称为doc.go
,其中有包文档、包注释、包声明,而没有其他内容,但这不是必须的。
在 Google 代码库和使用 Bazel 的项目中,Go 代码的目录布局与开源 Go 项目不同:你可以在一个目录中拥有多个go_library
目标。如果你期望在未来将你的项目开源,那么给每个包提供自己的目录是一个很好的理由。