Go语言中常见100问题-#12 项目结构最佳实践

创建一个好的Go项目结构并不是一件容易的事情,由于Go语言在设计包和模块方面提供了很大的自由度,因此在这方面没有通用的最佳实践。本文将首先讨论创建项目的常用组织结构,然后讨论一些最佳实践,给出改进项目组织方式的方法。

项目结构模板

Go语言维护者对构建Go项目结构没有严格的约定,在github上有一个称为标准Go项目结构的模板(github.com/golang-stan...%25EF%25BC%2589%25EF%25BC%258C%25E6%25B3%25A8%25E6%2584%258F%25E8%25AF%25A5%25E6%25A8%25A1%25E6%259D%25BF%25E4%25B8%258D%25E6%2598%25AFGo%25E5%25AE%2598%25E6%2596%25B9%25E6%258F%2590%25E4%25BE%259B%25E7%259A%2584%25E3%2580%2582%25E5%25A6%2582%25E6%259E%259C%25E6%2588%2591%25E4%25BB%25AC%25E7%259A%2584%25E9%25A1%25B9%25E7%259B%25AE%25E5%25BE%2588%25E5%25B0%258F%25EF%25BC%2588%25E5%258F%25AA%25E6%259C%2589%25E5%2587%25A0%25E4%25B8%25AA%25E6%2596%2587%25E4%25BB%25B6%25EF%25BC%2589%25EF%25BC%258C%25E6%2588%2596%25E8%2580%2585%25E5%2585%25AC%25E5%258F%25B8%25E5%2592%258C%25E9%25A1%25B9%25E7%259B%25AE%25E7%25BB%2584%25E5%25B7%25B2%25E7%25BB%258F%25E6%258C%2587%25E5%25AE%259A%25E4%25BA%2586%25E9%25A1%25B9%25E7%259B%25AE%25E7%25BB%2593%25E6%259E%2584%25E8%25A7%2584%25E8%258C%2583%25EF%25BC%258C%25E9%2587%258D%25E6%2596%25B0%25E8%25B0%2583%25E6%2595%25B4%25E6%2588%2596%25E8%25BF%2581%25E7%25A7%25BB%25E5%2588%25B0%25E4%25B8%258A%25E8%25BF%25B0%25E6%25A8%25A1%25E6%259D%25BF%25E6%25A0%25BC%25E5%25BC%258F%25E5%258F%25AF%25E8%2583%25BD%25E4%25B8%258D%25E5%2580%25BC%25E5%25BE%2597%25E3%2580%2582%25E5%25A6%2582%25E6%259E%259C%25E9%25A1%25B9%25E7%259B%25AE%25E8%25BF%2598%25E6%25B2%25A1%25E6%259C%2589%25E7%25BB%2593%25E6%259E%2584%25E8%25A7%2584%25E8%258C%2583%25EF%25BC%258C%25E9%2582%25A3%25E5%2589%258D%25E9%259D%25A2%25E8%25BF%2599%25E4%25B8%25AA%25E7%25BB%2593%25E6%259E%2584%25E5%2580%25BC%25E5%25BE%2597%25E5%258F%2582%25E8%2580%2583%25E5%2580%259F%25E9%2589%25B4%25E3%2580%2582%25E7%258E%25B0%25E5%259C%25A8%25E6%2588%2591%25E4%25BB%25AC%25E6%259D%25A5%25E7%259C%258B%25E7%259C%258B%25E8%25BF%2599%25E4%25B8%25AA%25E7%25BB%2593%25E6%259E%2584%25E6%25A8%25A1%25E6%259D%25BF%25E7%259A%2584%25E5%25B8%2583%25E5%25B1%2580%25EF%25BC%258C%25E9%2583%25BD%25E6%259C%2589%25E4%25BA%259B%25E4%25BB%2580%25E4%25B9%2588%25E5%2586%2585%25E5%25AE%25B9%25EF%25BC%259A "https://github.com/golang-standards/project-layout)%EF%BC%89%EF%BC%8C%E6%B3%A8%E6%84%8F%E8%AF%A5%E6%A8%A1%E6%9D%BF%E4%B8%8D%E6%98%AFGo%E5%AE%98%E6%96%B9%E6%8F%90%E4%BE%9B%E7%9A%84%E3%80%82%E5%A6%82%E6%9E%9C%E6%88%91%E4%BB%AC%E7%9A%84%E9%A1%B9%E7%9B%AE%E5%BE%88%E5%B0%8F%EF%BC%88%E5%8F%AA%E6%9C%89%E5%87%A0%E4%B8%AA%E6%96%87%E4%BB%B6%EF%BC%89%EF%BC%8C%E6%88%96%E8%80%85%E5%85%AC%E5%8F%B8%E5%92%8C%E9%A1%B9%E7%9B%AE%E7%BB%84%E5%B7%B2%E7%BB%8F%E6%8C%87%E5%AE%9A%E4%BA%86%E9%A1%B9%E7%9B%AE%E7%BB%93%E6%9E%84%E8%A7%84%E8%8C%83%EF%BC%8C%E9%87%8D%E6%96%B0%E8%B0%83%E6%95%B4%E6%88%96%E8%BF%81%E7%A7%BB%E5%88%B0%E4%B8%8A%E8%BF%B0%E6%A8%A1%E6%9D%BF%E6%A0%BC%E5%BC%8F%E5%8F%AF%E8%83%BD%E4%B8%8D%E5%80%BC%E5%BE%97%E3%80%82%E5%A6%82%E6%9E%9C%E9%A1%B9%E7%9B%AE%E8%BF%98%E6%B2%A1%E6%9C%89%E7%BB%93%E6%9E%84%E8%A7%84%E8%8C%83%EF%BC%8C%E9%82%A3%E5%89%8D%E9%9D%A2%E8%BF%99%E4%B8%AA%E7%BB%93%E6%9E%84%E5%80%BC%E5%BE%97%E5%8F%82%E8%80%83%E5%80%9F%E9%89%B4%E3%80%82%E7%8E%B0%E5%9C%A8%E6%88%91%E4%BB%AC%E6%9D%A5%E7%9C%8B%E7%9C%8B%E8%BF%99%E4%B8%AA%E7%BB%93%E6%9E%84%E6%A8%A1%E6%9D%BF%E7%9A%84%E5%B8%83%E5%B1%80%EF%BC%8C%E9%83%BD%E6%9C%89%E4%BA%9B%E4%BB%80%E4%B9%88%E5%86%85%E5%AE%B9%EF%BC%9A")

  • /cmd 项目主要的应用程序. foo应用程序的main.go应该位于/cmd/foo/main.go中。

  • /internal 私有的应用程序代码库,这里面的代码是不希望被其它人导入的。

  • /pkg 外面的应用程序可以使用的代码库,是向其它人公开的公共代码。

  • /test 存储测试数据和代码。Go语言中的单元测试文件与源文件通常都在一个包中。但像公共API测试或集成测试代码应该存放在/test中。

  • /configs 存放配置文件

  • /docs 存放设计和用户文档

  • /examples 应用程序或公共库函数的实例程序

  • /api api接口定义文件(Swagger, Protocol Buffers等)

  • /web web应用程序的资源文件(静态图片等)

  • /build 打包和持续集成(CI)文件

  • /scripts 用于分析、安装等脚本文件

  • /vendor 应用程序的依赖文件(例如Go模块的依赖库)

可以看到上面的标准结构中没有/src目录,这是因为/src目录太泛了,因此采用了/cmd、/internal和/pkg这种目录。

NOTE:在2021年,Go语言的核心维护者 Russ Cox对上面的项目结构表达反对意见。尽管它号称是Go项目标准结构,但不是官方的标准,有误导人嫌疑。对于项目结构,没有强制性约定必须采用上述模板。我们必须意识到这一个点,唯一注意的是项目中的各个模块结构要保持一致,达成统一。避免在不同的结构之间发生迁移,这会浪费时间。

包组织结构

在Go语言中,没有子包的概念。但是,我们可以在子目录中创建包。下面是标准库net中的目录结构。net既充当包,又充当包含其他包的目录。但是net/http包不继承net或对net包具有特定的访问权限。外界能看到net/http中可导出的元素。子目录的主要好处是将包中代码保存在具有高内聚性的地方。

console 复制代码
/net

/http

client.go

...

/smtp

auth.go

...

addrselect.go

...

对于Go包的组织形式,有不同的观点。例如,我们应该按业务类型还是按层来组织应用程序,这取决于自己的喜好。我们可能倾向于按业务类型(例如客户业务,合同业务等)对代码进行分组,或者我们倾向于遵循六边形原则对其进行分组。只要选择出了适合我们的方法,保持统一即可。

对于软件包,我们应该遵循一些最佳实践。首先,应该避免过渡设计,因为这可能会使得项目过于复杂。当我们搞清楚了项目包含的内容后,最好使用一个简单的形式组织并让项目不断的发展,而不是强迫自己预先制定完美的结构。

包的粒度是另一个需要考虑的重要因素,我们应该避免有几十个包含一两个文件的小包。如果这样设计,可能错过了这些包之间的一些逻辑联系,使得项目更难让人理解。此外,我们也应该避免使用包含很多文件的大包。总之,对于包的粒度,我们不应该走极端,导致包极小或极大。

包的命名也应该谨慎考虑。众所周知,命名是程序开发中一件困难的事情。为了帮助用户理解Go项目,我们应该根据它提供的内容命名包,而不是它包含的内容。此外,包名要有意义。因此,包的名称应该简短、简洁和富有表现力,按照惯例,应该是一个小写单词。

对于包导出什么,规则非常简单。我们应该尽可能减少应该导出的内容,以减少包之间的耦合并隐藏不必要导出的元素。如果不确定是否要导出一个元素,应该默认它不导出,在后面发现需要导出时,再调整代码支持将其导出。我们还要注意一些特殊情况,例如,当我们对一个结构体对象调用 encoding/json 标准库对其进行序列化或反序列化时,该结构体对象的字段需要是可导出的(即首字母要大写),否则会忽略该字段。

组织好一个项目结构并不是一件简单的事情,遵循上述这些规则有助于我们更容易维护。记住一点,保持结构一致对于简化可维护非常有帮助。因此,应确保代码库中的代码尽可能保持一致。

相关推荐
程序猿进阶4 小时前
如何在 Visual Studio Code 中反编译具有正确行号的 Java 类?
java·ide·vscode·算法·面试·职场和发展·架构
无名之逆5 小时前
云原生(Cloud Native)
开发语言·c++·算法·云原生·面试·职场和发展·大学期末
andrew_12199 小时前
腾讯 IEG 游戏前沿技术 一面复盘
java·redis·sql·面试
andrew_12199 小时前
腾讯 IEG 游戏前沿技术 二面复盘
后端·sql·面试
寻求出路的程序媛9 小时前
JVM —— 类加载器的分类,双亲委派机制
java·jvm·面试
kay_54510 小时前
YOLOv8改进 | 模块缝合 | C2f 融合SCConv提升检测性能【CVPR2023】
人工智能·python·深度学习·yolo·目标检测·面试·yolov8改进
gopher951111 小时前
qt相关面试题
开发语言·qt·面试
视觉小鸟12 小时前
【java面试每日五题之基础篇一】(仅个人理解)
java·笔记·面试
vd_vd21 小时前
内存区域-面试与分析
jvm·面试·职场和发展
蒙娜丽宁1 天前
Go语言错误处理详解
ios·golang·go·xcode·go1.19