小白学go基础03-了解Go项目的项目结构

我们先来看看第一个Go项目——Go语言自身——的项目结构是什么样的。Go项目的项目结构自1.0版本发布以来一直十分稳定,直到现在Go项目的顶层结构基本没有大的改变。

截至Go项目commit 1e3ffb0c(2019.5.14),Go1.0 项目结构如下:

$ tree -LF 1 ~/go/src/github.com/golang/go
./go
├── api/
├── AUTHORS
├── CONTRIBUTING.md
├── CONTRIBUTORS
├── doc/
├── favicon.ico
├── lib/
├── LICENSE
├── misc/
├── PATENTS
├── README.md
├── robots.txt
├── src/
└── test/

作为Go语言的创世项目,Go的项目结构的布局对后续的Go语言项目具有重要的参考意义,尤其是早期Go项目中src目录下面的结构,更是在后续被Go社区作为Go应用项目结构的模板广泛使用。

以早期的Go 1.3版本的src目录下的结构为例

$ tree -LF 1 ./src
./src
├── all.bash*
├── all.bat
├── all.rc*
├── clean.bash*
├── clean.bat
├── clean.rc*
├── cmd/
├── lib9/
├── libbio/
├── liblink/
├── make.bash*
├── make.bat
├── Make.dist
├── make.rc*
├── nacltest.bash*
├── pkg/
├── race.bash*
├── race.bat
├── run.bash*
├── run.bat
├── run.rc*
└── sudo.bash*

关于上面src目录下的结构,笔者总结了以下三个特点。

1)代码构建的脚本源文件放在src下面的顶层目录下。

2)src下的二级目录cmd下面存放着Go工具链相关的可执行文件(比如go、gofmt等)的主目录以及它们的main包源文件。

3)src下的二级目录pkg下面存放着上面cmd下各工具链程序依赖的包、Go运行时以及Go标准库的源文件。如下所示

$ tree -LF 1 ./pkg
./pkg
...
├── flag/
├── fmt/
├── go/
├── io/
├── log/
├── math/
...
├── syscall/
├── testing/
├── text/
├── time/
├── unicode/
└── unsafe/

在Go 1.3版本以后至今,Go项目下的src目录发生了几次结构上的变动。

● Go 1.4 版本删除了Go源码树中src/pkg/xxx中的pkg这一层级目录,改为直接使用src/xxx。

● Go 1.4版本在src下面增加internal目录,用于存放无法被外部导入、仅Go项目自用的包。

● Go 1.6版本在src下面增加vendor目录,但Go项目自身真正启用vendor机制是在Go 1.7版本中。vendor目录中存放了Go项目自身对外部项目的依赖,主要是golang.org/x下的各个包,包括net、text、crypto等。该目录下的包会在每次Go版本发布时更新。

● Go 1.13版本在src下面增加了go.mod和go.num,实现了Go项目自身的go module迁移。Go项目内所有包被放到名为std的module下面,其依赖的包依然是golang.org/x下的各个包。这个可以算是go 项目目录结构以及包管理的一个重大改动。

// Go 1.13版本Go项目src下面的go.mod
module std
go 1.12
require (
golang.org/x/crypto v0.0.0-20200124225646-8b5121be2f68
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7
golang.org/x/sys v0.0.0-20190529130038-5219a1e1c5f8 // indirect
golang.org/x/text v0.3.2 // indirect
)

下面是Go 1.16版本src目录下的完整布局:

├── Make.dist
├── README.vendor
├── all.bash*
├── all.bat
├── all.rc*
├── bootstrap.bash*
├── buildall.bash*
├── clean.bash*
├── clean.bat
├── clean.rc*
├── cmd/
├── cmp.bash
├── go.mod├── go.sum
├── internal/
├── make.bash*
├── make.bat
├── make.rc*
├── race.bash*
├── race.bat
├── run.bash*
├── run.bat
├── run.rc*
├── testdata/
...
└── vendor/

1. Go项目结构的最小标准布局

关于Go应用项目结构的标准布局是什么样子的,Go官方团队始终没有给出参考标准。不过作为Go语言项目的技术负责人,Russ Cox在一个开源项目的issue中给出了他关于Go项目结构的最小标准布局 的想法。他认为Go项目的最小标准布局应该是这样的

// 在Go项目仓库根路径下
- go.mod
- LICENSE
- xx.go
- yy.go
...- go.mod
- LICENSE
- package1- package1.go
- package2- package2.go
...

pkg、cmd、docs这些目录不应该成为Go项目标准结构的一部分,至少不是必需的。笔者认为Russ
Cox给出的最小标准布局与Go一贯崇尚的“简单”哲学是一脉相承的,这个布局很灵活,可以满足各种Go项目的需求。

但是在Russ Cox阐述上述最小标准之前,Go社区其实是处于“无标准”状态的,早期Go语言自身项目的结构布局对现存的大量Go开源项目的影响依然存在,对于一些规模稍大些的Go应用项目,我们势必会在上述“最小标准布局”的基础上进行扩展。而这种扩展显然不会是盲目的,还是会参考Go语言项目自身的结构布局,于是就有了下面的非官方标准的建议结构布局。

2. 以构建二进制可执行文件为目的的Go项目结构

基于Go语言项目自身的早期结构以及后续演进,Go社区在多年的Go语言实践积累后逐渐形成了一种典型项目结构,这种结构与Russ Cox的最小标准布局是兼容的,如下图所示:

在这里插入图片描述
上图所示就是一个支持(在cmd下)构建二进制可执行文件的典型Go项目的结构,我们分别来看一下各个重要目录的用途。

cmd目录:存放项目要构建的可执行文件对应的main包的源文件。如果有多个可执行文件需要构建,则将每个可执行文件的main包单独放在一个子目录中,比如图中的app1、app2。cmd目录下的各app的main包将整个项目的依赖连接在一起,并且通常来说,main包应该很简洁。我们会在main包中做一些命令行参数解析、资源初始化、日志设施初始化、数据库连接初始化等工作,之后就会将程序的执行权限交给更高级的执行控制对象。有一些Go项目将cmd这个名字改为app,但其功用并没有变。

pkg目录:存放项目自身要使用并且同样也是可执行文件对应main包要依赖的库文件。该目录下的包可以被外部项目引用,算是项目导出包的一个聚合。有些项目将pkg这个名字改为lib,但该目录的用途不变。由于Go语言项目自身在1.4版本中去掉了pkg这一层目录,因此有一些项目直接将包平铺到项目根路径下,但笔者认为对于一些规模稍大的项目,过多的包会让项目顶层目录不再简洁,显得很拥挤,因此个人建议对于复杂的Go项目保留pkg目录。

Makefile:这里的Makefile是项目构建工具所用脚本的“代表”,它可以代表任何第三方构建工具所用的脚本。Go并没有内置如make、bazel等级别的项目构建工具,对于一些规模稍大的项目而言,项目构建工具似乎不可缺少。在Go典型项目中,项目构建工具的脚本一般放在项目顶层目录下,比如这里的Makefile;对于构建脚本较多的项目,也可以建立build目录,并将构建脚本的规则属性文件、子构建脚本放入其中。

go.mod和go.sum:Go语言包依赖管理使用的配置文件。类似java 项目中pom.xml 文件。Go 1.11版本引入Go module机制,Go 1.16版本中,Go module成为默认的依赖包管理和构建机制。因此对于新的Go项目,建议基于Go module进行包依赖管理。对于没有使用Go module进行包管理的项目(可能主要是一些使用Go 1.11以前版本的Go项目),这里可以换为dep的Gopkg.toml和Gopkg.lock,或者glide的glide.yaml和glide.lock等。

● vendor目录(可选):vendor是Go 1.5版本引入的用于在项目本地缓存特定版本依赖包的机制。在引入Go module机制之前,基于vendor可以实现可重现的构建(reproducible build),保证基于同一源码构建出的可执行程序是等价的。Go module本身就可以实现可重现的构建而不需要vendor,当然Go module机制也保留了vendor目录(通过go mod vendor可以生成vendor下的依赖包;通过go build -mod=vendor可以实
现基于vendor的构建),因此这里将vendor目录视为一个可选目录。一般我们仅保留项目根目录下的vendor目录,否则会造成不必要的依赖选择的复杂性。

Go 1.11引入的module是一组同属于一个版本管理单元的包的集合。Go支持在一个项目/仓库中存在多个module,但这种管理方式可能要比一定比例的代码重复引入更多的复杂性。因此,如果项目结构中存在版本管理的“分歧”,比如app1和app2的发布版本并不总是同步的,那么笔者建议将项目拆分为多个项目(仓库),每个项目单独作为一个module
进行版本管理和演进

3. 以只构建库为目的的Go项目结构

Go 1.4发布时,Go语言项目自身去掉了src下的pkg这一层目录,这个结构上的改变对那些以只构建库为目的的Go库类型项目结构有一定的影响。我们来看一个典型的Go语言库类型项目的结构布局,如下图所示:

在这里插入图片描述
我们看到库类型项目结构与Go项目的最小标准布局也是兼容的,但比以构建二进制可执行文件为目的的Go项目要简单一些。

● 去除了cmd和pkg两个子目录:由于仅构建库,没必要保留存放二进制文件main包源文件的cmd目录;由于Go库项目的初衷一般都是对外部(开源或组织内部公开)暴露API,因此也没有必要将其单独聚合到pkg目录下面了。

● vendor不再是可选目录:对于库类型项目而言,不推荐在项目中放置vendor目录去缓存库自身的第三方依赖,库项目仅通过go.mod(或其他包依赖管理工具的manifest文件)明确表述出该项目依赖的模块或包以及版本要求即可。

4. 关于internal目录

无论是上面哪种类型的Go项目,对于不想暴露给外部引用,仅限项目内部使用的包,在项目结构上可以通过Go
1.4版本中引入的internal包机制来实现。

以库项目为例,最简单的方式就是在顶层加入一个internal目录,将不想暴露到外部的包都放在该目录下,比如下面项目结构中的ilib1、ilib2:

// 带internal的Go库项目结构
$tree -F ./chapter2/sources/GoLibProj
GoLibProj
├── LICENSE
├── Makefile
├── README.md
├── go.mod
├── internal/
│ ├── ilib1/
│ └── ilib2/
├── lib.go
├── lib1/
│ └── lib1.go
└── lib2/
└── lib2.go

这样,根据Go internal机制的作用原理,internal目录下的ilib1、ilib2可以被以GoLibProj目录为根目录的其他目录下的代码(比如lib.go、lib1/lib1.go等)所导入和使用,但是却不可以为GoLibProj目录以外的代码所使用,从而实现选择性地暴露API包

所以了解完不同的项目类型所对应的项目结构,开发者可以在这样上面的项目结构核心的基础上根据实际需要进行扩展。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/121031.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ansible学习笔记15

1、roles:(难点) roles介绍: roles(角色):就是通过分别将variables,tasks及handlers等放置于单独的目录中,并可以便捷地调用他们的一种机制。 假设我们要写一个playbo…

KaTex用法

KaTeX是一个用于数学公式渲染的JavaScript库&#xff0c;可以在网页上方便地显示数学符号和公式。下面是KaTeX的使用方法&#xff1a; 在网页中引入KaTeX的CSS和JS文件&#xff1a; <link rel"stylesheet" href"https://cdnjs.cloudflare.com/ajax/libs/Ka…

【真题解析】系统集成项目管理工程师 2023 年上半年真题卷(综合知识)

本文为系统集成项目管理工程师考试(软考) 2023 年上半年真题(全国卷),包含答案与详细解析。考试共分为两科,成绩均 ≥45 即可通过考试: 综合知识(选择题 75 道,75分)案例分析(问答题 4 道,75分)综合知识(选择题*75)1-10 题11-20 题21-30 题31-40 题41-50 题51-60 …

webpack(三)loader

定义 loader用于对模块的源代码进行转换&#xff0c;在imporrt或加载模块时预处理文件 webpack做的事情&#xff0c;仅仅是分析出各种模块的依赖关系&#xff0c;然后形成资源列表&#xff0c;最终打包生成到指定文件中。 在webpack内部&#xff0c;任何文件都是模块&#x…

VBA技术资料MF52:VBA_在Excel中突出显示前 10 个值

【分享成果&#xff0c;随喜正能量】一言之善&#xff0c;重于千金。善良不分大小&#xff0c;有时候你以为的一句话&#xff0c;小小的举手之劳&#xff0c;也可能就是别人的救赎&#xff01;不要吝啬你的善良&#xff0c;因为你永远不知道那小小的善良能给多少人带来光明。。…

大厂面试 | 百度一面,顶不住

题目来源&#xff1a;https://www.nowcoder.com/feed/main/detail/d39aabc0debd4dba810b4b9671d54348 前文 本期是【捞捞面经】系列文章的第 2 期&#xff0c;持续更新中…。&#xff08;更多与往期下方仓库直达&#xff09; 《捞捞面经》系列正式开始连载啦&#xff0c;据说看…

2023年7月婴幼儿辅食市场数据分析(京东商品数据)

随着人们对婴幼儿饮食健康的关注不断增加&#xff0c;市场对高品质、安全、营养丰富的辅食需求也日益旺盛。婴幼儿辅食市场增长放缓&#xff0c;但整体仍保持上升态势。鲸参谋数据显示&#xff0c;今年7月份&#xff0c;京东平台婴幼儿辅食市场的销量为1000万&#xff0c;同比增…

分布式环境下的数据同步

一般而言elasticsearch负责搜索&#xff08;查询&#xff09;&#xff0c;而sql数据负责记录&#xff08;增删改&#xff09;&#xff0c;elasticsearch中的数据来自于sql数据库&#xff0c;因此sql数据发生改变时&#xff0c;elasticsearch也必须跟着改变&#xff0c;这个就是…

NS2安装及入门实例——(ns2.35 / Ubuntu20.04)

文章目录 一、ns2安装1、更新系统源2、准备工作3、下载安装包4、安装5、问题① 问题1② 问题2③ 问题3 6、安装成功7、环境配置 二、nam安装1、安装2、问题 三、实例 一、ns2安装 1、更新系统源 sudo apt-get update sudo apt-get upgrade2、准备工作 sudo apt-get install …

5个强大的Java分布式缓存框架推荐

在开发中大型Java软件项目时&#xff0c;很多Java架构师都会遇到数据库读写瓶颈&#xff0c;如果你在系统架构时并没有将缓存策略考虑进去&#xff0c;或者并没有选择更优的缓存策略&#xff0c;那么到时候重构起来将会是一个噩梦。 在开发中大型Java软件项目时&#xff0c;很…

LeetCode 2511 最多可以摧毁的敌人城堡数目

LeetCode 2511 最多可以摧毁的敌人城堡数目 力扣题目链接&#xff1a;力扣题目链接 给你一个长度为 n &#xff0c;下标从 0 开始的整数数组 forts &#xff0c;表示一些城堡。forts[i] 可以是 -1 &#xff0c;0 或者 1 &#xff0c;其中&#xff1a; -1 表示第 i 个位置 没…

sql:SQL优化知识点记录(九)

&#xff08;1&#xff09;小表驱动大表 对sql调优的分析&#xff1a; 排序优化&#xff1a; 数据库的连接方式&#xff0c;里面的数据尽量这样连接&#xff0c;尽量选择第一个方式&#xff0c;因为两个表的连接一共建立5次连接&#xff0c;第二个建立1000次连接&#xff0c;从…

Matlab中fdatool结合STM32F4设计滤波器

数字滤波器的原理 1.从功能上分&#xff1b;低通、带通、高通、带阻。滤波器口诀&#xff1a;低通滤高频&#xff1b;高通滤低频&#xff1b;带通滤两边&#xff1b;带阻阻中间&#xff1b; 2.从实现方法上分:FIR、IIR 3.从设计方法上来分&#xff1a;Chebyshev(切比雪夫&…

准备HarmonyOS开发环境

引言 在开始 HarmonyOS 开发之前&#xff0c;需要准备好开发环境。本章将详细指导你如何安装 HarmonyOS SDK、配置开发环境、创建 HarmonyOS 项目。 目录 安装 HarmonyOS SDK 配置开发环境 创建 HarmonyOS 项目 总结 1. 安装 HarmonyOS SDK HarmonyOS SDK 是开发 Harmo…

Springboot 实践(13)spring boot 整合RabbitMq

前文讲解了RabbitMQ的下载和安装&#xff0c;此文讲解springboot整合RabbitMq实现消息的发送和消费。 1、创建web project项目&#xff0c;名称为“SpringbootAction-RabbitMQ” 2、修改pom.xml文件&#xff0c;添加amqp使用jar包 <!-- RabbitMQ --> <dependency&g…

电压互感器倍频感应耐压试验方法

试验方法 升压设备的容器应足够&#xff0c; 试验前应确认高压升压等设备功能正常&#xff1b; 按上图接好线&#xff0c; 三倍频发生器、 高压器外壳必须可靠接地。 将三倍频电源发生装置的输出线与被试电压互感器的一组二次绕组接线端连接好&#xff08;如 a-n 端&#xff0…

Spring MVC 五 - Spring MVC的配置和DispatcherServlet初始化过程

今天的内容是SpringMVC的初始化过程&#xff0c;其实也就是DispatcherServilet的初始化过程。 Special Bean Types DispatcherServlet委托如下一些特殊的bean来处理请求、并渲染正确的返回。这些特殊的bean是Spring MVC框架管理的bean、按照Spring框架的约定处理相关请求&…

2D项目经验总结

2D项目经验总结 前言地图的绘制Sprite Editor叠层注意点&#xff08;SortingLayer相关知识点&#xff09;Tile Paltette的使用Animated Tiles&#xff08;动起来的图片&#xff08;也称作瓷片或者瓦砖&#xff09;&#xff09; 玩家移动玩家方向的翻转刚体注意点 碰撞器输入系统…

手写Mybatis:第19章-二级缓存

文章目录 一、目标&#xff1a;二级缓存二、设计&#xff1a;二级缓存三、实现&#xff1a;二级缓存3.1 工程结构3.2 二级缓存类图3.3 二级缓存队列3.3.1 FIFI缓存策略3.3.2 事务缓存3.3.3 事务管理3.3.4 修改一级缓存 3.4 缓存执行器3.4.1 执行器接口3.4.2 执行器抽象基类3.4.…

华为OD机试 - 等和子数组最小和 - 深度优先搜索(Java 2022 Q4 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷&#xff09;》…