在Go中,包是一个非常重要的概念,我们利用包来封装不同语义单元的功能,加强复用代码
首先是Go中的包,所有Go语言的程序都会组织为若干的文件,每组文件被称为一个包,每个包的代码都可以是一个复用单元,被其他的项目引用,比如http包,是如何利用包的特性组织功能的
这一目录包含了多个包,这些包分为了实现HTTP客户端和服务端 测试段 性能调节工具等小包
方便我们单独的导入和使用
所有的.go文件,除了空行和注释,都应该在第一行声明自己所属的包,每个包都在一个单独的目录中
对于包名的命名
我们一般是使用包所在目录的名字,我们可以清楚知道包名,对于net/http 在http目录下的所有文件都属于http
包名全小写
而且并不是要完全不同名,导入包的时候是使用全路径的,所以可以再导入后,对包名进行修改,显示别名
然后是main包
Go中,对于main包,是由特殊含义的
当编译的时候,发现了某个包的名字为main,同时要在包中发现名为main()的函数,不然编译出错,main函数式程序的入口,而且编译的时候,会使用main包所在目录的目录名作为可执行文件的文件名
比如我们创建一个hello.go的文件,在hello目录下
声明为main包,以及main函数
我们在hello目录下,执行命令 go build
如果是window端,会获得一个hello.exe 除此外都是hello文件,都可以进行相关的直接执行
然后是导入
我们看下如何将代码组织导入包中
import语句告诉编译器 到哪里去找包,
导入包需要关键字import和对应的代码块
导入的实例如下
import (
“fmt”
“io/ioutil”
“os”
“../words”
)
编译器会使用Go环境变量设置的路径,来查找并导入包
找到的顺序时有规律的
会现在安装位置找,然后是GoPath的指定目录,最后是个人工作空间
比如Go的安装目录为 /user/local/go
然后环境变量为
/home/myproject
这样查找包的顺序如下,我们以net/http包为例
我们查找顺序为
先去标准库,然后工作空间
一旦编译器满足了一个import语句的包,就会停止进一步的查找,会首先查找Go的安装目录
而且支持远程导入
比如从GitHub Launchpad 等地方导入
Go也是支持的,其本身会通过导入的路径来确定获取的代码是否在网络上
例如
import “github.com/spf13/viper”
go build会使用GOPATH的设置,在磁盘上搜索这个包,这个导入路径代表了一个URL,指向了GitHub上的代码库
或者通过go get命令完成,go get获取任意指定的URL的包
对于导入的包的别名命名
比如我们有多个convert,是从不同的地方导入的
我们可以通过命名导入来导入,命名导入可以让我们在import语句的的包的左侧重新定义一个名字
比如
“fmt”
myfmt “_mylib/fmt”
从而做到起别名
导入了一个不在代码的包的时候,会编译时失败
所以我们可以再导入的时候使用 空白标识符 _ 来重命名这个导入
函数init
对于之前包说的每个包含的多个init函数,这些函数会在程序执行开始之前被调用,init函数可以用于设置包,初始化变量,或者一些引导的工作
数据库驱动就会在启动的时候利用init函数将自身注册到sql包中
从而方便回调
利用下面的init
func init() {
d = new(PostgresDriver)
sql.Register(“postgres”, d)
}
如果我们导入了这个包,就会调用init函数,方便注入
然后展示一下使用空白标识符来导入包,这个新导入的包不需要使用,但是会被扫描到从而自动调用init函数
package main
import (
“database/sql”
_ “./postgres”
)
// main is the entry point for the application.
func main() {
sql.Open(“postgres”, “mydb”)
}
然后是工具链
我们看一下Go自带的命令行
通过输出的列表来看,其中包含了一个编译器,可以通过build命令启动
build和clean的命令分别执行编译和清理的工作
然后就是run命令
利用run命令,我们可以进行一次性的构建和执行
go run word.go
会先构建wordcount.go,然后执行构建后的程序
然后是Go的开发工具
go vet可以帮助我们检测代码的常见错误,
常见错误可以有
Printf类函数调用的时候,类型匹配错误的参数
定义常用的方法,方法签名的错误
错误的结构标签
指定字段名的结构字面量
然后是格式化代码
我们可以利用fmt命令,自动格式化开发人员指定的源代码文件并保存
常见的就是
执行go fmt后,得到
然后是go语言生成文档
生成文档的方式有两种
可以直接利用go doc的命令来打印文档,或者启动一个web服务器
通过点击来查看文档
命令行的获取当时很简单,加入我们需要用Go来开发读取Unix tar文件的程序,了解相关的tar文档
可以利用如下的命令
go doc tar
package tar // import “archive/tar”
Package tar implements access to tar archives. Tape archives (tar) are a file format for storing a sequence of files that can be read and written in a streaming manner. This package aims to cover most variations of the format, including those produced by GNU and BSD tar tools. const TypeReg = ‘0’ … var ErrHeader = errors.New(“archive/tar: invalid tar header”) … type Format int const FormatUnknown Format … type Header struct{ … } func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) type Reader struct{ … } func NewReader(r io.Reader) *Reader type Writer struct{ … } func NewWriter(w io.Writer) *Writer |
这样,方便我们直接翻看文档
对于web端的文档查看,可以使用如下的命令
godoc -http=[:port]
这样就在端口上启动了Web服务器
而且无论是web还是控制台,都是可以显示自己编写的代码文档
对于如何生成文档中
我们只需要遵守,将自己的文档作为注释放进到代码中就可以了
或者直接在工程中包含一个叫做doc.go的文件,使用相同的包名,并将包的介绍放在包名声明之前
其次是Go中的版本管理和依赖控制
在进行版本控制和依赖管理之前,首先说,如果我们想要做一些开源的包供他人使用,需要遵循一些特点
1.包要在代码库的根目录,这样,分享的时候,包的源代码就是代码库目录的结构的,对于导入时候语句变长,所以只需要将源文件放在公用代码库的根目录就可以了
2.包可以很小,可能只有几个API
3.对于编写完的,需要执行go fmt,提高自己代码的可读性
4.注意给代码编写文档
然后就是对其他第三方的依赖管理
因为Go本身不支持管理包的依赖,所以流行的依赖管理工具是godep和vender和gopkg,in工具
第三方的依赖
像是godep和vender这种社区工具,是使用的路径重写解决了路径依赖
将所有的依赖包复制到工程代码中,然后进行重写路径
比如godep
下面就是重写的路径
依赖的原代码放在了_workspace/src的目录中
如果需要import,则导入语句如下
这样将所有的依赖防在一个单独的工程代码库,更容易构建工程
然后是gb,可以是一个新的构建工具
gb不包装Go的工具链,不使用GOPATH,gb基于工程将GO工具链空间的元信息进行替换
这样就不修改了导入路径
我们尝试编写一个gb的工程
一个gb工程就是磁盘上一个包含 src 子目录,符号$PROJECT导入了工程的根目录中,其下有一个src/的目录,这个用来描述工程在磁盘上的位置
gb工程会区分开发人员写的代码和开发人员需要依赖的代码,开发人员的代码依赖的代码会称为第三方代码
工程中存放开发人员写的代码的位置
$PROJECT/src/
第三方代码的位置
$PROJECT/vendor/src
使用起来的也不需要重写导入路径,import基本如下
gb工具会在 $PROJECT/src/目录中查找,如果找不到就会在 $PROJECT/vender/src/目录中查找
这样,就不需要重写,就可以完成整个构建过程,这也是gb的流行原因
但是一旦使用了gb,可能和官方的工具链不兼容
对于测试和构建gb工程,都需要先进入 $PROJECT目录,然后使用gb工具
gb build all
这样,就可以进行构建和测试了
GO语言中包是组织代码的基本代码
环境变量GOPATH决定了Go源代码在磁盘上保存,编译的位置
每个工程可以设置不同的GOPATH
开发人员可以使用go get来获取别的包,并安装在自己的GOPATH
推荐使用依赖管理工具来管理依赖
诸如 godep vender gb