Golang 项目布局浅析
女主宣言
Golang作为当下云开发中最为流行的语言之一,越来越受到广大程序员的青睐。开发Golang项目经常遇到的一个常见问题是如何组织项目结构布局。今天作者从项目结构以及对内部、外部包的引用来讲讲布局问题,希望对大家有所帮助。
PS:丰富的一线技术、多元化的表现形式,尽在“360云计算”,点关注哦!
开始编码之前,我们需要先明确一些问题:
项目结构如何反映代码的引入方式?
除代码外,如何组织项目的命令行工具?
如何灵活的在不同模块间组织项目代码?
多个包如何在一个模块中共存?
我们先明确一些名词和概念:
Internal packages,内部私有包,只能从其模块中的其他包引入,不能被外部包引入。用户可以使用 go get 安装需要的外部包。
1
Helloworld
我们打开一个项目,项目路径是模块名。项目的 go.mod 文件包含以下行:
module github.com/qidian/modlib
Go项目通常通过其GitHub路径命名。Go还支持自定义名称,本文暂不赘述,之后开新帖再说。我们可以暂时用`github.com/your-handle/your-project`或`your-project-domain.io`替换`github.com/qidian/modlib`。
模块名称非常重要,因为他是项目代码中引入包名的基础:
2
项目布局
先来看一个项目 modlib 的目录和文件布局:
├── LICENSE
├── README.md
├── config.go
├── go.mod
├── go.sum
├── clientlib
│ ├── lib.go
│ └── lib_test.go
├── cmd
│ ├── modlib-client
│ │ └── main.go
│ └── modlib-server
│ └── main.go
├── internal
│ └── auth
│ ├── auth.go
│ └── auth_test.go
└── serverlib└── lib.go
让我们从根目录中的文件开始。
go.mod 是模块定义文件。它包含上面显示的模块名称,我的项目没有依赖项。依赖项与项目布局设计无关。有需要的同学可以从 Golang 官方博客学习。
go.sum 由 go tools 管理,其包含所有依赖项校验值。
config.go,这是我们查看的第一个代码文件,它包含一个简单的 Config()函数:
package modlibfunc Config() string {return "modlib config"
}
第一行申明包名,由于该文件位于模块的顶层,因此其程序包名称即为模块名称。
关于如何引入 github.com/qidian/modlib ,我们来看一个例子:
package mainimport "fmt"
import "github.com/qidian/modlib"func main() {fmt.Println(modlib.Config())
}
因此,如果您的模块提供单个 package,或者您要从模块的顶层软件包中导出代码,则将其所有代码放在模块的顶层目录中,并命名该 package 作为模块路径的最后一部分(除非您使用更灵活的 vanity imports)。
3
引用外部包
clientlib / lib.go 是我们对 clientlib 模块封装的文件。文件名可以根据业务逻辑来命名:
package clientlibfunc Hello() string {return "clientlib hello"
}
我们再来看下面这个例子,通过 github.com/qidian/modlib/clientlib 导入 clientlib:
package mainimport "fmt"
import "github.com/qidian/modlib"
import "github.com/qidian
/modlib/clientlib"func main() {fmt.Println(modlib.Config())fmt.Println(clientlib.Hello())
}
serverlib目录包含了另一个用户可以引入的package。这里展示了多个程序包如何在代码结构中并存。
关于包嵌套,它可以根据需要增加目录层级。我们可见的 package 名称由模块根目录的相对路径确定。例如,如果我们有一个 `clientlib/tokens` 的子目录 ,并在tokens包中包含一些代码,则用户将使用如下代码引入该目录。
import "github.com/qidian/modlib/clientlib/tokens“
对于一些模块而言,一个顶级 package 就足够业务开发了。在本例中没有用户可导入的 package 子目录,但是所有代码都在 modlib 的单个或多个 Go 文件中。
4
Commands
一些Go项目还需要制作可执行程序,我们一般会再增加一个cmd目录。
该目录是项目所有命令行程序的常规位置。程序的命名方案通常为:
用户可以使用go工具按如下方式安装此类命令:
$ go get github.com/qidian/modlib/cmd/cmd-name# Go downloads, builds and installs cmd-name into the default location.
# The bin/ directory in the default location is often in $PATH, so we can
# just invoke cmd-name now$ cmd-name ...
在modlib中,提供了两个不同的命令行程序作为示例:modlib-client和modlib-server。在每个代码中,代码都在包main中;文件名为main.go。
这是我们在测试环境上运行的命令:
$ go get github.com/qidian/modlib/cmd/modlib-client
$ modlib-client
Running client
Config: modlib config
clientlib hello$ go get github.com/qidian/modlib/cmd/modlib-server
$ modlib-server
Running server
Config: modlib config
Auth: thou art authorized
serverlib hello# Clean up...
$ rm -f `which modlib-server` `which modlib-client`
我们来看看 modlib-serve.go 是如何从 modlib 中导入其他代码的:
package mainimport ("fmt""github.com/qidian/modlib""github.com/qidian/modlib/internal/auth""github.com/qidian/modlib/serverlib"
)func main() {fmt.Println("Running server")fmt.Println("Config:", modlib.Config())fmt.Println("Auth:", auth.GetAuth())fmt.Println(serverlib.Hello())
}
Golang 里的绝对导入适用于引入package和二进制命令,这是 clientlib 中的代码需要引入 modlib 时的例子:
github.com/eliben/modlib
5
私有包
另一个重要概念是私有包,也就是项目内部使用的软件包,并且我们不想导出给外部用户。由于语义版本控制,这在Go模块中尤其重要。您的项目在v1中导出的所有内容都将成为公共API,并且必须遵守语义版本兼容性。
Go工具将内部包识别为特殊路径,只有同一模块中的软件包可以引入它。如果我们尝试在外部模块代码中引用,则会抛错:
use of internal package github.com/eliben/modlib/internal/auth not allowed
在本文样例中,internal中只有一个package。而在实际的工程项目里通常会有一堆完整的package目录树。
将内部API重构并将其导出给其他同学很容易,但是使用外部API并取消导出会很麻烦。所以我在开发时会尽可能地将模块需要的私有包和代码放入内部包中。
最后再举个例子,如果一个网站项目 repo 中,我们将代码安排在 internal/website中。用于项目的内部工具和脚本也是如此。这样一来项目的根目录是最清晰并且对开发者来说更友好。理想情况下,开发者通过项目代码布局就可以大致了解他们想了解的东西所在位置,因此将一些代码放在内部会很有意义。
相关文章
https://blog.golang.org/v2-go-modules
https://blog.golang.org/module-compatibility
360云计算
由360云平台团队打造的技术分享公众号,内容涉及数据库、大数据、微服务、容器、AIOps、IoT等众多技术领域,通过夯实的技术积累和丰富的一线实战经验,为你带来最有料的技术分享
Golang 项目布局浅析相关推荐
- go 项目 cmd目录_Golang 项目布局浅析
奇技 · 指南 Golang作为当下云开发中最为流行的语言之一,越来越受到广大程序员的青睐.开发Golang项目经常遇到的一个常见问题是如何组织项目结构布局.今天作者从项目结构以及对内部.外部包的引用 ...
- Go 语言编程 — 项目布局规范
目录 文章目录 目录 项目布局(Project Layout)规范 程序目录 /cmd(Command)包 /internal /pkg(Package) /vendor Service(服务端)应用 ...
- Go 编码建议——项目布局
文章目录 1.前言 2.项目布局 2.1 整体风格 2.2 Go 代码目录 /cmd /internal /pkg /vendor 2.3 服务应用程序目录 /api 2.4 Web 应用程序目录 / ...
- 全球及中国LCP行业应用项目布局及产能规模预测报告2021版
全球及中国LCP行业应用项目布局及产能规模预测报告2021版 HS--HS--HS--HS--HS--HS--HS--HS--HS--HS--HS--HS-- [修订日期]:2021年11月 [搜索 ...
- Flask 项目布局
一个最简单的 Flask 应用可以是单个文件. hello.py from flask import Flaskapp = Flask(__name__)@app.route('/') def hel ...
- Global项目|浅析销售BOM实施方案及注意事项
[原创]Global项目|浅析销售BOM实施方案及注意事项 https://mp.weixin.qq.com/s/dyA3vCDfidRSHVPRoSH4wQ 前言: 本文意图结合实际Global项目 ...
- golang 依赖管理_简介:如何管理Golang项目依赖项
golang 依赖管理 by Ying Kit Yuen 英杰苑 简介:如何管理Golang项目依赖项 (An intro to dep: How to manage your Golang proj ...
- golang项目持续集成Travis-CI实践
文章目录 shorturl 1 `Travis-ci`支持 2 `.travis.yml` 3 Hello World 4 短链服务 5 添加持续集成 6 问题fix实践 7 还得是GOPATH 8 ...
- Python Flask框架-开发简单博客-项目布局、应用设置
作者:Eason_LYC 悲观者预言失败,十言九中. 乐观者创造奇迹,一次即可. 一个人的价值,只在于他所拥有的.所以可以不学无术,但不能一无所有! 技术领域:WEB安全.网络攻防 关注WEB安全.网 ...
最新文章
- 后端必备 Git 分支开发:规范指南
- Java Web整合开发(41) -- Forum
- Java进阶:Set、Map线程安全问题
- OpenCV 中的图像处理 004_平滑图像
- jQuery 打气球小游戏 点击气球爆炸效果
- Python DbUtil操作数据
- 旧金山运输系统攻击者威胁将公布消费者和职工个人数据
- PAT——1054. 求平均值
- 详解 ASP.NET并行,异步,多线程
- Axure RP安装破解汉化以及发布到至AxureShare
- 2017 matlab 仿真,Matlab 2017a 安装程序
- .dSYM文件的生成
- linux的定时器的使用,linux定时器如何使用方法
- 服务器的上行带宽和下行带宽是什么意思
- HTML Javascript CGI
- 数字人民币智慧学生证来了,对于特定群体硬钱包或大有可为
- 新墨斯智能鞋就是您的足底按摩师
- (两百五十六)学习perfetto(一)
- RestTemplate和ResponseEntity
- Javaweb安全——Weblogic反序列化漏洞(一)
热门文章
- Struts2输入校验之validate输入校验方式
- debug没反应 eclipse_解决eclipse无法运行或调试的办法
- (非)对称加密算法在https中的应用(加密过程以及CA颁发、验证)
- Java形参的改变不会影响实参
- 作用域经典练习题(主要是这个图太大不能一起分享)
- 《大型网站技术架构》1.大型网站架构演练
- Cannot forward after response has been committed
- oracle-pl/sql之一
- java enum类探索
- android 关于listview scrollview 底部 控件无法显示的两个解决方案