写一个其他人可以使用的模块
一个或多个相关的包被组织成一个模块。这些包都包含一些功能类似的函数。例如,你可以创建一个模块,它包含了一些包,这些包实现了各种财务分析的函数,那么编写财务应用程序的人就可以使用这个模块。
Go 代码被组织成包,包被组织成模块。你使用的包的模块指定了 Go 运行代码所需要的上下文,它包括编写代码的Go版本及其所需的其他模块集合。
在模块中添加或改进功能时,你会发布模块的新版本。使用你模块的开发人员可以导入最新的版本进行测试,以便在生产环境中使用。
步骤如下:
mkdir greetings
cd greetings
root@ubuntu:~/gogo/greetings# go mod init example.com/greetings
go: creating new go.mod: module example.com/greetings
package greetings
import "fmt"
// Hello returns a greeting for the named person.
func Hello(name string) string {
// Return a greeting that embeds the name in a message.
message := fmt.Sprintf("Hi, %v. Welcome!", name)
return message
}
从另一个模块中使用你的代码
mkdir hello
cd hello
package main
import (
"fmt"
"example.com/greetings"
)
func main() {
// Get a greeting message and print it.
message := greetings.Hello("Gladys")
fmt.Println(message)
}
root@ubuntu:~/gogo/hello# go mod init hello
go: creating new go.mod: module hello
module hello
go 1.13
replace example.com/greetings => ../greetings
module hello
go 1.13
replace example.com/greetings => ../greetings
require example.com/greetings v0.0.0-00010101000000-000000000000
require example.com/greetings v1.1.0
root@ubuntu:~/gogo/hello# ./hello
Hi, Gladys. Welcome!
添加测试用例
原文中返回并处理一个错误、返回一个随机的问候语、为多人返回问候语,这几节就不提了。看下原文,将代码粘贴下来跑跑,就可以了,这些代码中提到的返回并处理错误、字典、切片等知识点后续还会提到的。
go test
下面的步骤是我们如何为"greetings"模块添加测试用例:
package greetings
import (
"testing"
"regexp"
)
// TestHelloName calls greetings.Hello with a name, checking
// for a valid return value.
func TestHelloName(t *testing.T) {
name := "Gladys"
want := regexp.MustCompile(`\b`+name+`\b`)
msg, err := Hello("Gladys")
if !want.MatchString(msg) || err != nil {
t.Fatalf(`Hello("Gladys") = %q, %v, want match for %#q, nil`, msg, err, want)
}
}
// TestHelloEmpty calls greetings.Hello with an empty string,
// checking for an error.
func TestHelloEmpty(t *testing.T) {
msg, err := Hello("")
if msg != "" || err == nil {
t.Fatalf(`Hello("") = %q, %v, want "", error`, msg, err)
}
}
$ go test
PASS
ok example.com/greetings 0.364s
$ go test -v
=== RUN TestHelloName
--- PASS: TestHelloName (0.00s)
=== RUN TestHelloEmpty
--- PASS: TestHelloEmpty (0.00s)
PASS
ok example.com/greetings 0.372s
编译并安装应用程序
go rungo install
在安装之前,我们最好设置下安装目录
go env -w GOBIN=/home/liu/go/bin
go install
23/7/5 补充说明:
一
如果一个模块【文件夹】中没有子文件夹【这个模块只含有一个包】,那么导入的包的路径就是模块的路径,导入后的包的名称是在各.go文件中声明的 package xxx 中的 xxx。一般情况,xxx 和文件夹名称是一样的,所以经常说导入的模块名称就是路径的最后一个分量!
如果一个模块中有多个子文件夹,那么这个模块包含多个包,那么导入的包的路径是模块路径加上子文件夹【相对模块的】路径。
如果这个模块里也定一个了一个包,那么这个包的导入路径就是模块的路径。
二
hello.go 中的 import “example.com/greetings” 和 greetings 文件夹中的使用 go mod init 初始化的模块名“example.com/greetings ”没有任何关系,关键点在于要和go.mod 中的“replace example.com/greetings => …/greetings”对应。
如果将 import “example.com/greetings” 改成 import “a/b/c”,然后 “replace a/b/c => …/greetings” ,效果是一样的。
三
import 导入的是包的路径,也就是一个文件夹的路径,文件夹下可以有多个go文件,但是这些go文件中的包声明必须相同【都包含 package xxx的声明】。
示例中 greetings 文件夹下的源文件名为 “greetings.go”,实际上它可以是任意的名字。它的文件夹名称也可以任意起,模块的名字【go mod init】也可以任意起,关键是在 import 时要能根据路径找到相应的文件夹。
示例中的模块是演示给可以互联网共享使用的,所以把模块名定为"example.com/greetings",就是一个URL,import 的路径也是一个URL。
看一个例子:
目录结构:
├── a
│ ├── aa.go
│ ├── b
│ │ └── bb.go
│ ├── c
│ │ └── cc.go
│ └── go.mod
└── hello
├── d
│ └── dd.go
├── ee.go
├── go.mod
└── hello.go
目录a:
a/aa.go
package aaa
import "fmt"
func D() {
fmt.Println("i'm D from package aaa")
}
a/go.mod
module fds3
go 1.19
a/b/bb.go
package bbb
import "fmt"
func D() {
fmt.Println("i'm D from package bbb")
}
a/c/cc.go
package ccc
import "fmt"
func D() {
fmt.Println("i'm D from package ccc")
}
a目录下定义了fds3模块,包含3个 package: aaa,bbb,ccc。
目录 hello:
hello/d/dd.go
package ddd
import "fmt"
func D() {
fmt.Println("i'm D from package ddd")
}
hello/ee.go
package main
import "fmt"
func D() {
fmt.Println("i'm D from package main")
}
hello/go.mod
module hello2
go 1.19
replace dddfff => ../a
require dddfff v0.0.0-00010101000000-000000000000 // indirect
hello/hello.go
package main
import "hello2/d" // package ddd 在 hello2 模块的子目录中!
import "dddfff" // package aaa
import "dddfff/b" //package bbb
import "dddfff/c" //package ccc
func main() {
aaa.D()
bbb.D()
ccc.D()
ddd.D() //hello/d/ 目录下的包
D() // hello/ee.go 文件中定义的函数
}
hello目录下定义了main模块,包含 package ddd
输出:
administrator@administrator-PC:~/gogo/hello$ go run .
i'm D from package aaa
i'm D from package bbb
i'm D from package ccc
i'm D from package ddd
i'm D from package main