写一个其他人可以使用的模块

一个或多个相关的包被组织成一个模块。这些包都包含一些功能类似的函数。例如,你可以创建一个模块,它包含了一些包,这些包实现了各种财务分析的函数,那么编写财务应用程序的人就可以使用这个模块。

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