解密Golang工程结构

在写本教程之前,我也写过其它教程,甚至出版过一部关于Docker的技术书籍<Docker全攻略>。有的时候会发现码了很多字之后,写的话词不达意,言不由衷,严重脱离文章主体思想。 无奈之下,只能大段删掉,然后痛定思痛,重新来过。 究其原因,就是在下笔之前没有好好规划文章脉络,写着写着就离题万里了。

Golang作为一门开发语言,写代码和写文章其本质是相似的。既然在写文章之前,我们需要规划脉络,同样在写代码之前,我们也需要提前规划Golang工程结构,才能做到心中有数,指下有路。

Golang工程结构,从宏观方面来说分为两部分,根空间(GOROOT)和工作空间(GOPATH)。 其中根空间也就是GOROOT是go的安装目录,用来搜寻和加载go的工具链,例如执行go build,go fmt等等。 而GOPATH是你的工作空间,也就是放置go代码的地方。

每个工作空间里面一定会存在三个目录: src,pkg和bin。

src目录用来存放所有的源代码,这些源代码有可能是你自己写的,也有可能是别人写的。pkg目录则用来放置生成的静态链接库(不懂什么是静态链接库? 没关系,不知道就先不知道吧。在下面章节中会涉及到这方面的内容,如果读完此教程之后仍然搞不清楚尽情的发邮件问我吧)。而最后的bin目录就用来放置构建出来的可执行的二进制工具。

这三个目录中,只有src目录是必须要存在的。 而pkg和bin这两个目录,go在涉及到时会自动创建。

我们假设当前GOPATH是 $HOME/golang,那么设置下面的环境变量来告之Golang.

  1. export GOPATH=$HOME/golang

然后来创建src目录,

  1. mkdir $GOPATH/src

在src目录里面,就可以放任意的.go源代码了。 虽说是任意放置,但也不能把所有的.go文件都放到src这一层里面。Golang"不喜欢"杂乱无章的工程(我假定你也不喜欢..),golang"喜欢"通过package来组织代码结构。

好,何为package呢?

package就是目录的路径。 例如在src目录中创建一个testcode目录,那么testcode就是package。 如果testcode里面还有一个子目录code1,那么testcode/code1就是package。

Package有何作用呢?Golang语言同其他语言一样,需要一个唯一标示来保证源代码的不重复性。而使用目录路径+文件名已经被几十年的程序语言开发史所证明是十分有效且靠谱的,所以Golang不落俗套的采用了目录路径当做package来标记每个源代码。

当Golang在build源代码时,会根据package的名称去GOPATH/src中读取相对应的源码。例如:假设你在代码中引用了 examplecode/util,那么golang就会加载$GOPATH/src/examplecode/util这个路径下所有的.go文件。

在作为目录标示的同时Golang也捎带手的做了一些小升华,让package兼具了区分用户的责任。

Golang建议package采用github.com/<用户名>的组织方式,比如我自己的包就是github.com/andy-zhangtao。因此通过package的名称,我们就可以知道这个包出自何人之手。需要读者注意的是,这只是推荐做法,不是强制做法。如果写成bitbucket.org/andy-zhangtao, 行不行? 当然行,同样没问题。只要你能保证在GOPATH/src中存在bitbucket.org/andy-zhangtao目录并且里面有编译所依赖的.go文件就行。

好,看到这里,估计你的Golang安装包应该是下载完毕了。 按照你的平台特点,点击安装包来安装golang。 安装完成之后,设置GOROOT。记住这个是Go的安装目录,也就是到go这一层就可以了。实在心里没底,肾发虚,通过下面代码检查一下:

  1. ls $GOROOT
  2. AUTHORS CONTRIBUTORS PATENTS VERSION bin doc lib pkg src
  3. CONTRIBUTING.md LICENSE README.md api blog favicon.ico misc robots.txt test

上面输出的目录名称可能略有不同,只要大致是这样的,那就说明你配置对了。 如果不是,或者差的老远。听我一声劝, 返回去重新看一遍文章,看看哪里搞错了。

然后在检查一下GOPATH有没有设置好,如果检查通过没问题。我们就先来爽一下。在GOPATH/src里面创建一个temp目录(当然也可以叫做其它名称,高兴就好),然后再创建一个main.go,粘贴下面代码到main.go里面:

  1. package main
  2. import "fmt"
  3. func main() {
  4. fmt.Printf("Hello God, My Golang.\n")
  5. }

保存退出,执行:

  1. go build

如果执行之后没有任何输出,恭喜你第一步成功了。 找一下生成的二进制文件,(默认和目录名保持一致),执行./temp,看到输出高不高兴,惊不惊喜~~

如果没看到,我的天😱呀。 上点心,按照上面的步骤重新来一遍,一定是某个环境变量没配好,或者某个字母没敲对。自己检查一下,如果实在检查不出来,出去闷一根烟,思考一下人生,自己大声喊一句:世界如此美好,这个bug我一定要找到!

第二节到此为止,比上一节有诚意吧, 看码的字就知道诚意满满。刚开始接触golang,怕闪腰,所以小步慢跑。着了道,再气运丹田,一飞冲天。 别着急,没有一口气吃个胖子的,也没有一口气能写完教程的。 我也要去喝点水,补个觉了。 如果读着还不错,点个star,给我点动力。