Go 官方推荐的 Go 项目文件组织方式

刚开始接触 Go 的开发者大概都会遇到一个问题:我该如何组织我的 Go 项目?这种问题当然没有标准答案,不过 Go 官方下场,给了广大 Gopher 一个推荐模板。本文就来带大家一起来学习一下 Go 官方对于 Go 项目布局的指导原则。

本文以 Go 官方博客「Organizing a Go module」为基石进行讲解。

基础 Go 包

对于最基础的 Go 包,我们可以按照如下方式组织:

bash 复制代码
project-root-directory/
  go.mod
  modname.go
  modname_test.go

所有代码都位于项目的根目录下,这个项目由一个模块(module)组成,而这个模块又由一个包(package)组成。

假设这个项目会上传到 GitHub 仓库 github.com/someuser/modname 中,那么 go.mod 文件中的第一行(module line)应写为:

go 复制代码
module github.com/someuser/modname

modname.go 文件中的代码使用如下命令声明一个 modname 包:

go 复制代码
package modname

// ... package code here

在其他 Go 项目中,可以通过如下方式导入这个 Go 包:

go 复制代码
import "github.com/someuser/modname"

此外,一个 Go 包其实可以被拆分成多个文件:

bash 复制代码
project-root-directory/
  go.mod
  modname.go
  modname_test.go
  auth.go
  auth_test.go
  hash.go
  hash_test.go

这里所有 Go 文件都属于同一个包,即都会声明为 package modname。

NOTE:

不知道你有没有注意,示例中的项目名称 project-root-directory 使用短横线来连接多个单词,而 Go 文件名则使用单词直接连写的形式 modname.go(而非 mod_name.go),这也是 Go 的一贯风格,虽然没有强制约定,但看多了社区中优秀的 Go 代码,你就会有所体会。

关于这点更加深入的探讨,你可以阅读我曾写过文章《Go 项目文件命名规范是什么?》来进行学习。

可执行的 Go 程序

Go 可执行程序(或命令行程序)一般根据复杂程度来构建目录,最小的 Go 程序可以仅定义一个包含 main 函数的 Go 文件,如果功能较多,可以将其拆分为多个文件:

bash 复制代码
project-root-directory/
  go.mod
  auth.go
  auth_test.go
  client.go
  main.go

main.go 中存放了 main 函数,不过这只是一个约定俗成的做法,你可以将其放到任何合法的 xxx.go 中。

当拆分为多个文件,如果 main.go 引用了其他文件中的代码,这个时候就不能简单的使用 go run main.go 来执行 Go 程序了,可以按照如下方式执行:

bash 复制代码
$ go run .

并且,用户可以使用以下命令将 Go 程序安装到自己的主机上:

bash 复制代码
$ go install github.com/someuser/modname@latest

更复杂的 Go 程序

如果一个 Go 包或者 Go 可执行程序更加复杂,则可以按照如下方式组织目录结构:

bash 复制代码
project-root-directory/
  internal/
    auth/
      auth.go
      auth_test.go
    hash/
      hash.go
      hash_test.go
  go.mod
  modname.go
  modname_test.go

这里还演示了使用 modname.go 作为程序的入口,而非命名为 main.go。

internal/ 目录下的所有包都不会被公开,其他程序无法引用。这是 Go 语言层面限制,强制的,不仅仅是约定俗成。这样,我们就可以随时随地的重构 internal/ 目录中的代码,而不必担心影响其他程序。

当然,modname.go 中可以导入 internal/ 目录下的包,因为它们本来就是一个程序。在 modname.go 中导入 auth 包:

go 复制代码
import "github.com/someuser/modname/internal/auth"

包含多个包的 Go 程序

我们知道,一个 Go 模块(module)可以由多个包(package)组成,而每个包都可以有自己的子目录。

我们可以组织成一个层次结构更深的 Go 程序:

bash 复制代码
project-root-directory/
  go.mod
  modname.go
  modname_test.go
  auth/
    auth.go
    auth_test.go
    token/
      token.go
      token_test.go
  hash/
    hash.go
  internal/
    trace/
      trace.go

这个目录看起来更符合一个真实的 Go 包。

假设 go.mod 中声明 module 如下:

go 复制代码
module github.com/someuser/modname

而 modname.go 位于项目根目录,声明包为 modname,用户在使用时则可以按照如下方式导入此包:

go 复制代码
import "github.com/someuser/modname"

也可以按照如下方式导入子包:

go 复制代码
import "github.com/someuser/modname/auth"
import "github.com/someuser/modname/auth/token"
import "github.com/someuser/modname/hash"

注意:internal/trace 无法被外部程序导入。

包含多个可执行的 Go 程序

有时候,我们可能会在一个 Git 仓库中包含多个可执行的 Go 程序,可以按照如下方式组织目录:

bash 复制代码
project-root-directory/
  go.mod
  internal/
    ... shared internal packages
  prog1/
    main.go
  prog2/
    main.go

prog1/ 和 prog2/ 中分别有自己的 main.go 文件,即两个独立的可执行 Go 程序。

prog1/ 和 prog2/ 可以共享根目录中的其他包或子包。

我们可以按照如下方式分别安装二者到主机上:

bash 复制代码
$ go install github.com/someuser/modname/prog1@latest
$ go install github.com/someuser/modname/prog2@latest

其实,针对一个 Git 仓库中包含多个可执行的 Go 程序的情况,我们还有更好的做法来组织目录:

bash 复制代码
project-root-directory/
  go.mod
  modname.go
  modname_test.go
  auth/
    auth.go
    auth_test.go
  internal/
    ... internal packages
  cmd/
    prog1/
      main.go
    prog2/
      main.go

将可执行文件都放到 cmd/ 目录下也是约定俗成的做法。下次,当你见到 cmd/ 目录,就应该想到这个目录是干什么的。

现在可执行程序的安装命令如下:

bash 复制代码
$ go install github.com/someuser/modname/cmd/prog1@latest
$ go install github.com/someuser/modname/cmd/prog2@latest

服务器项目

其实,我们在用 Go 编写程序时,大多数是用来实现 Go Web Server 程序,这也是 Go 最常见的使用场景,Go 是实现服务器的通用语言选择。

比如 Kubernetes 项目,其最核心的组件也是 Server 程序。

通常,我们可以按照如下方式来组织 Go 服务器项目:

bash 复制代码
project-root-directory/
  go.mod
  internal/
    auth/
      ...
    metrics/
      ...
    model/
      ...
  cmd/
    api-server/
      main.go
    metrics-analyzer/
      main.go
    ...
  ... the project's other directories with non-Go code

如果你有过 Go Web 开发经验,是不是很熟悉这个目录结构?

这已经到了广大 Gopher 最熟悉的领域,接下来,我就不过多讲解了,你可以继续阅读我写的另一篇文章《如何设计一个优秀的 Go Web 项目目录结构》,里面介绍了开源项目 github.com/golang-stan... 对 Go Web 项目组织的最佳实践。

总结

我们学习了 Go 官方推荐的项目布局的指导原则,现在我们对于 Go 包和 Go 可执行程序的目录组织方式,都有了较为清晰的认知。

此外,我们还顺便学习了如何安装 Go 可以执行程序,可以通过 go install xxx 来安装。

对于广大 Gopher,接触最多的应该是 Go Web 服务器的开发。对于 Go Web 项目,不仅 Go 官方给出了指导原则,我们还可以参考 project-layout 项目学习一个大型的 Go 项目是如何布局的。

希望此文能对你有所启发。

联系我

相关推荐
时间的拾荒人5 小时前
Qt 界面美化实战:QSS 样式表
开发语言·qt·面试
码事漫谈6 小时前
三步改掉 AI 味,附可直接复制的去 AI 味提示词
后端
IT_陈寒6 小时前
Redis卡顿的锅,这次真不是大key的错
前端·人工智能·后端
可乐鸡翅yeah_6 小时前
video.js 集成 hls.js 开发 M3U8 播放器,新手高频踩坑
开发语言·前端·javascript·后端·ecmascript·m3u8·音视频在线播放
程序猿_极客6 小时前
【免费】分享一套优质的基于SpringBoot的服装商城管理系统的设计与实现(带可视化图表、协同过滤功能),源码+文档+视频详解(讲解)
java·spring boot·后端·服装商城管理系统
明月_清风7 小时前
面对陌生的 GitHub 项目无从下手?这 4 个网站帮你快速读懂源码
前端·后端·github
bug菌7 小时前
🤔同事突然问我:Spring的注解 @Component 和 @Service 有何不同?
java·spring boot·后端
leobertlan7 小时前
痛苦系列 | DSP-02 频域切片:DTFT与DFT的探索
android·后端
加我攻城狮7 小时前
当大模型把「景区」定位到附近公共厕所:我的 AI 旅行小程序踩坑实录
后端·微信
Lonely丶墨轩7 小时前
用 Vue + Spring Boot + Python 做一个 AI 双人海龟汤游戏:从出题、联机到自动审稿等核心技术设计与实现
spring boot·后端·游戏