go 语言中 package 的特点
示例
在 ch8 这个 directory 下再新建一个 client directory 和一个 series directory,my_series.go 文件中是一个求 Fibonacci 数列的函数。
my_series.go
package series
import "errors"
// 获取 fibonacci 数列,首字母大写表示可以在包外被访问
// 如果是 getFibonacci 则无法被包外访问
func GetFibonacci(n int) ([]int, error) {
if n <= 1 || n >= 100 {
return nil, errors.New("the input num n is illegal")
}
prev, last := 1, 2
fibList := []int{1, 2}
for i := 3; i <= n; i++ {
var tmp = prev + last
fibList = append(fibList, tmp)
//同时赋值,将last的值赋值给prev,将tmp的值赋值给last
prev, last = last, tmp
}
return fibList, nil
}
package_test.go
package client
import (
"ch8/series"
"testing"
)
func TestPackageManagement(t *testing.T) {
if fibList, err := series.GetFibonacci(5); err == nil {
t.Log(fibList)
return
} else {
t.Log(fibList)
}
}
控制台输出
=== RUN TestPackageManagement
package_test.go:10: [1 2 3 5 8]
--- PASS: TestPackageManagement (0.00s)
PASS
如果我们将 my_series.go 中的 GetFibonacci 修改为 getFibonacci。
就会发现无法访问 getFibonacci 方法
如果又修改为 GetFibonacci,就可以发现又可以开始访问了
如下时运行时遇到的问题:package xxx is not in GOROOT,按照如下方法即可解决
package ch8/client is not in GOROOT (C:\Program Files\Go\src\ch8\client)
go env -w GO111MODULE=off
如下所示,我在 my_series.go 添加了两个 init 方法,这两个 init。
package series
import (
"errors"
"fmt"
)
func init() {
fmt.Println("执行init方法1")
}
func init() {
fmt.Println("执行init方法2")
}
// 获取 fibonacci 数列,首字母大写表示可以在包外被访问
// 如果是 getFibonacci 则无法被包外访问
func GetFibonacci(n int) ([]int, error) {
if n <= 1 || n >= 100 {
return nil, errors.New("the input num n is illegal")
}
prev, last := 1, 2
fibList := []int{1, 2}
for i := 3; i <= n; i++ {
var tmp = prev + last
fibList = append(fibList, tmp)
//同时赋值,将last的值赋值给prev,将tmp的值赋值给last
prev, last = last, tmp
}
return fibList, nil
}
进行调用
package client
import (
"ch8/series"
"testing"
)
func TestPackageManagement(t *testing.T) {
if fibList, err := series.GetFibonacci(5); err == nil {
t.Log(fibList)
return
} else {
t.Log(fibList)
}
}
从输出中可以看到,两个init方法都被执行了,按照定义的先后顺序进行执行。
执行init方法1
执行init方法2
=== RUN TestPackageManagement
package_test.go:10: [1 2 3 5 8]
--- PASS: TestPackageManagement (0.00s)
PASS
比如这里有一个第三方package,这是一个 concurrent_map,github地址为:https://github.com/easierway/concurrent_map
由于 github 访问速度太慢,我将其导入到我的 gitee 仓库中。
地址为:https://gitee.com/simonzhaojia/concurrent_map.git
从该仓库中我们可以看出只有 package 的源码,没有 src 等路径,我们提交代码到 git 仓库时,也应该只提交 package 的源码,不要将 src 的路径也提交上去,这样就可以通过 go get 命令获取到。
首先看看我的工程目录,可以发现此时的 src 路径还没有第三方package。
然后我在windows 的 terminal 中执行如下命令,需要注意的是,在命令行中执行命令时,一定要到工程的目录下去执行。
D:\dev\go\go_learning>go get -u gitee.com/simonzhaojia/concurrent_map
D:\dev\go\go_learning>
-u 参数表示每次导入时都需要从网上去检查最新版,有最新版就更新。地址没有以 https:// 开头,直接以域名开头,也不需要地址最后以 .git 结尾。
执行完毕以后就会发现在工程目录下多了一个文件夹,里面就是导入的第三方 package
使用
package remote_package
import (
// cm 是给import的package 起的一个别名
cm "gitee.com/simonzhaojia/concurrent_map"
"testing"
)
func TestConcurrentMap(t *testing.T) {
m := cm.CreateConcurrentMap(99)
m.Set(cm.StrKey("key1"), 10)
if value , ok := m.Get(cm.StrKey("key1")); ok {
t.Logf("value = %d",value)
}
}
输出
=== RUN TestConcurrentMap
remote_package_test.go:12: value = 10
--- PASS: TestConcurrentMap (0.00s)
PASS
go 依赖的问题。在依赖管理方面,go 还不如Java成熟,在Java中可以使用 Maven 和 Gradle 来进行依赖管理。
演示 go 语言中的依赖管理之前,先了解一下 go 中的 vendor 路径。
Go语言从 1.5 版本开始开始引入 vendor 模式,如果项目目录下有 vendor 目录,那么Go语言编译器会优先使用 vendor 内的包进行编译、测试等。
go 语言中常用的依赖管理工具
在 go1.11时出现了 go module,官方推荐使用 go module 来进行依赖管理,go module 由于是新出来不久的,使用和实践也还比较少,更多的还是上面列举的工具。
如下,演示一下 glide 的使用。
首先是先安装 glide,可以参照如下教程。
Windows 下安装 Glide 的教程
在上述的例子中,我们在 remote_package 这个包中,使用了一个第三方的包 concurrent_map,如下演示如何使用 glide 管理 remote_package 这个包使用的第三方包。
package remote_package
import (
// cm 是给import的package 起的一个别名
cm "gitee.com/simonzhaojia/concurrent_map"
"testing"
)
func TestConcurrentMap(t *testing.T) {
m := cm.CreateConcurrentMap(99)
m.Set(cm.StrKey("key1"), 10)
if value , ok := m.Get(cm.StrKey("key1")); ok {
t.Logf("value = %d",value)
}
}
进入 remote_package 这个 package 所在的路径下执行 glide init,然后一路 Yes 下去。
D:\dev\go\go_learning\src\ch8\remote_package>glide init
[INFO] Generating a YAML configuration file and guessing the dependencies
[INFO] Attempting to import from other package managers (use --skip-import to skip)
[INFO] Scanning code to look for dependencies
[INFO] --> Found test reference to gitee.com\simonzhaojia\concurrent_map
[INFO] Writing configuration file (glide.yaml)
[INFO] Would you like Glide to help you find ways to improve your glide.yaml configuration?
[INFO] If you want to revisit this step you can use the config-wizard command at any time.
[INFO] Yes (Y) or No (N)?
Y
[INFO] Looking for dependencies to make suggestions on
[INFO] --> Scanning for dependencies not using version ranges
[INFO] --> Scanning for dependencies using commit ids
[INFO] Gathering information on each dependency
[INFO] --> This may take a moment. Especially on a codebase with many dependencies
[INFO] --> Gathering release information for dependencies
[INFO] --> Looking for dependency imports where versions are commit ids
[INFO] Here are some suggestions...
[INFO] The package gitee.com/simonzhaojia/concurrent_map appears to have Semantic Version releases (http://semver.org).
[INFO] The latest release is v1.0.0. You are currently not using a release. Would you like
[INFO] to use this release? Yes (Y) or No (N)
Y
[INFO] Would you like to remember the previous decision and apply it to future
[INFO] dependencies? Yes (Y) or No (N)
Y
[INFO] Updating gitee.com/simonzhaojia/concurrent_map to use the release v1.0.0 instead of no release
[INFO] The package gitee.com/simonzhaojia/concurrent_map appears to use semantic versions (http://semver.org).
[INFO] Would you like to track the latest minor or patch releases (major.minor.patch)?
[INFO] Tracking minor version releases would use '>= 1.0.0, < 2.0.0' ('^1.0.0'). Tracking patch version
[INFO] releases would use '>= 1.0.0, < 1.1.0' ('~1.0.0'). For more information on Glide versions
[INFO] and ranges see https://glide.sh/docs/versions
[INFO] Minor (M), Patch (P), or Skip Ranges (S)?
S
[INFO] Would you like to remember the previous decision and apply it to future
[INFO] dependencies? Yes (Y) or No (N)
Y
[INFO] Configuration changes have been made. Would you like to write these
[INFO] changes to your configuration file? Yes (Y) or No (N)
Y
[INFO] Writing updates to configuration file (glide.yaml)
[INFO] You can now edit the glide.yaml file.:
[INFO] --> For more information on versions and ranges see https://glide.sh/docs/versions/
[INFO] --> For details on additional metadata see https://glide.sh/docs/glide.yaml/
D:\dev\go\go_learning\src\ch8\remote_package>
执行完毕以后,可以发现在 remote_package 这个package 下生成了一个 glide.yaml
glide.yaml 中的内容如下。里面有 remote_package 这个package 所使用依赖的相应版本。
package: ch8/remote_package
import: []
testImport:
- package: gitee.com/simonzhaojia/concurrent_map
version: v1.0.0
现在我将之前导入的依赖包删除掉
因为依赖包都被删除掉了,之前的代码自然就报错了。
然后在 remote_package 的路径下执行 glide install 命令,输出如下。
D:\dev\go\go_learning\src\ch8\remote_package>glide install
[INFO] Lock file (glide.lock) does not exist. Performing update.
[INFO] Downloading dependencies. Please wait...
[INFO] --> Fetching gitee.com/simonzhaojia/concurrent_map.
[INFO] --> Setting version for gitee.com/simonzhaojia/concurrent_map to v1.0.0.
[INFO] Resolving imports
[INFO] Downloading dependencies. Please wait...
[INFO] Setting references for remaining imports
[INFO] Exporting resolved dependencies...
[INFO] --> Exporting gitee.com/simonzhaojia/concurrent_map
[INFO] Replacing existing vendor dependencies
[INFO] Project relies on 0 dependencies.
D:\dev\go\go_learning\src\ch8\remote_package>
可以发现在 remote_package 的路径下生成了一个 vendor 文件夹,里面下载了第三方的包(和之前使用 go get 命令下载第三方包的路径不同,go get 命令下载的第三方包在 src 路径下)。此时,verdor 路径已经添加到程序的扫描包的路径下,程序自然也没有报错了。
参考
Go语言从入门到实战 蔡超