目录

  • 第十章 包和工具
  • 包简介
  • 导入路径
  • 包声明
  • 导入声明
  • 包和命名
  • 工具
  • 工作区结构
  • 下载包
  • 构建包
  • 包文档
  • 内部包
  • 查询包

第十章 包和工具

包简介

  • 包系统的设计是为了简化大型程序的设计和维护工作
  • 每个包都定义了一个不同的名字空间用于它内部的每个标识符的访问
  • 每个包还通过控制包内名字的可见性和是否导出实现封装特性(大小写开头)
  • 当我们修改一个源文件,我们必须重头构建
  • Go语言编译器的编译速度明显快于其它编译语言的,得益于三个特性:
    1.所有导入的包必须在每个文件的开头显式声明,这样的话编译器就没有必要读取和分析整个源文件来判断包的依赖关系
    2.禁止包的环状依赖,因为没有循环依赖,包的依赖关系形成一个有向无环图,每个包可以被独立编译,而且很可能是被并发编译
    3.编译后包的目标文件不仅仅记录包本身的导出信息,目标文件同时还记录了包的依赖关系
  • 因此,在编译一个包的时候,编译器只需要读取每个直接导入包的目标文件,而不需要遍历所有依赖的的文件

导入路径

  • Go语言的规范并没有指明包的导入路径字符串的具体含义,导入路径的具体含义是由构建工具来解释的

包声明

  • 每个Go程序源文件,开头必有package包声明语句
  • 包声明语句目的在于确定当前包被其它包导入时的默认标识符(包名)
  • 通常来说,默认的包名就是包导入路径名的最后一段,当然也有例外:
    1.名字为main的包是给go bulid构建命令使用的,与路径无关,这个包编译完后必须调用连接器生成一个可执行程序
    2.包所在目录中可能有一些文件名是以test.go为后缀的源文件,并且这些源文件声明的包名也是以_test为后缀的,这种目录可以包含两种包:普通包和测试用的外部扩展包,两者相互独立,测试用的包,用go test独立编译
    3.一些依赖版本号的管理工具会在导入路径后追加版本号信息,例如“gopkg.in/yaml.v2”,这种情况用的是yaml

导入声明

  • import 关键字,使用圆括号和双引号
import "fmt"
import "os"
  • 上下等价
import (
	"fmt"
	"os"
)
  • 导入包可以通过添加空行来分组:通常把来自不同组织的包独自分组
  • 包的导入顺序无关紧要,但是每个分组中的一般会按字符串顺序排列
import (
	"fmt"
	"html/template"
	"os"
	
	"golang.org/x/net/html"
	"golang.org/x/net/ipv4"
)
  • 导入包有时候会有相同名字,可以重命名:
import (
	"crypto/rand"
	mrand "math/rand" // alternative name mrand avoids conflict
)
  • 包的匿名导入:
    1.如果只是导入一个包而不使用导入的包将会导致编译错误
    2.有时候只是想利用导入包,而不是明确去使用包内的变量,可以使用下划线来重命名导入的包,这样它会计算包级变量的初始化表达式和init初始化函数
import _ "image/png" // register PNG decoder

包和命名

  • 包名:
    1.简洁明了:包名尽量短小,但不能难以理解
    2.避免使用可能被经常用于局部变量的名字
    3.单数:包名一般采用单数形式
    4.避免二义性:避免包名还有其它含义

工具

  • Go语言工具箱集合了一系列功能的命令集

工作区结构

  • GOPATH环境变量
  • GOPATH指定的工作目录有三个子目录
    1.src:用于存储源代码
    2.pkg:用于保存编译后的包的目标文件
    3.bin:保存编译后的可执行程序
  • GOROOT环境变量
    1.Go的按照目录
    2.自带标准库包的位置
  • go env命令用来查看所有环境变量

下载包

  • 使用命令go get 可以下载一个单一的包或者用...下载整个子目录里面的每个包
  • 一旦go get 命令下载了包,然后就是安装包或包对应的可执行的程序
  • go get -u命令只是简单地保证每个包是最新版本,如果是第一次下载包则是比较很方便的;但是对于发布程序则可能是不合适的,因为本地程序可能需要对依赖的包做精确的版本依赖管理
  • 可以使用vendor来管理包,go help gopath可以看到相关文档

构建包

  • go build 命令编译命令行参数指定的每个包:默认情况下,go build命令构建指定的包和它依赖的包,然后丢弃除了最后的可执行文件之外所有的中间编译结果。
  • go install命令和go build命令很相似,但是它会保存每个包的编译成果,而不是将它们都丢弃。被编译的包会被保存到$GOPATH/pkg目录下,目录路径和 src目录路径对应,可执行程序被保存到$GOPATH/bin目录

包文档

  • Go语言的编码风格鼓励为每个包提供良好的文档
  • go doc 打印包的声明和每个成员的文档注释

内部包

  • 没有导出的标识符只在同一个包内部可以访问,而导出的标识符则是面向Import它的包可见的
  • 有时候,一个中间的状态可能也是有用的,对于一小部分信任的包是可见的,但并不是对所有调用者都可见。例如,当我们计划将一个大的包拆分为很多小的更容易维护的子包,但是我们并不想将内部的子包结构也完全暴露出去。同时,我们可能还希望在内部子包之间共享一些通用的处理包,或者我们只是想实验一个新包的还并不稳定的接口,暂时只暴露给一些受限制的用户使用。
  • 为了满足这些需求,Go语言的构建工具对包含internal名字的路径段的包导入路径做了特殊处理。这种包叫internal包,一个internal包只能被和internal目录有同一个父目录的包所导入
  • net/http/internal/chunked内部包只能被net/http/httputil或net/http包导入,但是不能被net/url包导入。不过net/url包却可以导入net/http/httputil包。
net/http
net/http/internal/chunked
net/http/httputil
net/url

查询包

  • go list命令查询包,
  • go list ...表示匹配任意的包的导入路径,可以用它来列表工作区中的所有包