golang 项目开发如何创建 Module
原创 demo007x 知识派 2023-12-22 08:30 发表于北京 听全文golang 项目开发如何创建 Module
img为什么要创建一个 Module?
我们日常开发程序的时候都会引入第三方的 package,使用第三方的 package 的好处是我们可以快速的开发我们的程序的功能,只需要专注我们自己项目的功能,而且第三方的 package 提供了强大的,丰富的功能模块。比如 web 开发框架 gin, echo,微服务开发框架 go-micro、go-zero;权限控制:casbin,jwt: jwt-go, gorm:gorm, 存储:minio,编写命令:cobra, 配置文件解析:viper,数据库:mysql 等等。这些包都是以 module 的形式提供的服务。
在自己的团队内部同样可以构建自己的私有化的 module,工公司内部的其他团队使用。比如我们公司有三个项目组,都有一些共同的项目。所以我们有公共的 module 放到 gitlab上面提供不同团队的公共使用。同样这样也遵循了软件设计的高内聚低耦合的设计总则。将独立的功能封转为 module。
这样做的好处是我们可以只实现一次共享使用,并不需要每个团队中都各自实现,同样也会带啦有好多弊端。
如何实现我们自己的 Module?
既然我们使用了很多开源的 module为我们的日常开发提供了很多的便捷性,那我们该如何实现自己的 module 来提供给团队中使用,甚至可以开放到 gitlab 上面提供给所有人使用(取之于开源,馈与开源)。
通过一下步骤来实现一个其他人可以使用的 module
-
1. 创建模块:编写一个小模块,其中包含可以从另一个模块调用的函数。
-
2. 从另一个模块调用您的代码:导入并使用您的新模块。
-
3. 返回并处理错误: 添加简单的错误处理。
-
4. 处理切片中的数据(Go 的动态大小数组)。
-
5. 添加测试程序:使用 Go 的内置单元测试功能来测试您的代码。
创建一个 module 目录
首先创建一个 Go 模块。在模块中,我们可以将一组有用的功能在一个或多个相关包中实现。
Go 代码被分组为 package,package 又被分组为module。module 中需要指定运行代码所需的依赖项,包括 Go 版本及其所需的其他模块。
1、打开控制台并cd
转到工作目录中,并创建目录module/greetings
:
mkdir module
cd module/greetings/
2、go mod init
命令初始化模块 。
运行go mod init
命令,为其提供模块路径 - 此处使用example.com/greetings
. 如果发布模块,则这必须是 Go 工具可以下载您的模块的路径。那将是我们的代码的存储库。
$ go mod init example.com/greetings
该go mod init
命令创建一个 go.mod 文件来跟踪代码的依赖项。当我们添加依赖项时,go.mod 文件将列出项目的代码所依赖的版本。这可以保持构建的可重复性,并让我们可以直接控制要使用的模块版本。
3、打开编辑器在greetings
创建目录下创建 文件greetings.go
并编写代码:
package greetings
import (
"fmt"
"math/rand"
)
func Hello(name string) string {
return fmt.Sprintf("嗨,%v。欢迎!", name)
}
该函数Hello 接受一个name
类型为的参数 string
。该函数还返回一个string
。在Go中,名称以大写字母开头的函数可以被不在同一包中的函数调用。这在 Go 中称为导出名称。
从另一个模块调用您的代码
1、我们在 greeting 的同级目录创建 hello 目录,作为调用者。
~/Developer/module
tree -d
.
├── greetings
└── hello
在 hello 目录中我们执行命令 go mod init
初始化module 模块
go mod init example.com/hello
2、打开编辑器并创建文件 hello.go 文件。在 hello.go 文件中编写代码:
import (
"example.com/greetings"
"log"
)
func main() {
message := greetings.Hello("demo007")
fmt.Println(message)
}
在文件中声明一个main
包。在 Go 中,作为应用程序执行的代码必须位于包中main
。
导入两个包:example.com/greetings
和fmt
。 导入 example.com/greetings
可以访问该Hello
函数。还可以导入fmt
, 以及处理输入和输出文本的函数(例如将文本打印到控制台)。greetings
通过调用包的 函数 来获取输出信息。
阿里云ECS11月销量王 99元/年 2核2G 3M固定带宽不限流量,新老同享,活动期间新购、续费同价,开发必备!
https://www.aliyun.com/lowcode/promotion/allinaliyun/99program?source=5176.11533457&userCode=c0ngnrad
3、编辑example.com/hello
模块以使用本地 example.com/greetings
模块。
example.com/greetings
对于生产使用, 可以从其存储库发布模块,Go 工具可以在其中找到它并下载它。但是由于我们尚未发布该模块,因此我们需要调整该模块,以便它可以在本地文件系统上 example.com/hello
找到代码example.com/greetings
。
所以我们使用go mod edit
命令编辑example.com/hello
模块,将 Go 工具从其模块路径(模块所在的位置)重定向到本地目录(模块所在的位置)。
在 hello 目录中的命令提示符下,运行以下命令:
$ go mod edit -replace example.com/greetings=../greetings
该命令指定example.com/greetings
应替换为../greetings
以便查找依赖项。运行命令后,hello 目录中的 go.mod 文件应包含一条replace的指令。
module example.com/hello
go 1.20
replace example.com/greetings => ../greetings
require example.com/greetings v0.0.0-00010101000000-000000000000
然后我们在命令中 cd hello 的目录中,并执行命令 go mod tidy
, 执行完成后我们发现新增加一行 require example.com/greetings v0.0.0-00010101000000-000000000000
并制定了一个版本号.
我们在 hello 中执行运行命令查看:
go run hello.go
嗨,demo007。欢迎!
返回并处理错误
处理错误是可靠代码的一个基本特征。下面我们将添加一些代码以从 greetings 模块返回错误,然后在调用者中处理它。
在 greetings/greetings.go
中,修改代码:
package greetings
import (
"errors"
"fmt"
"math/rand"
)
func Hello(name string) (string, error) {
// 如果没有提供名称,则返回带有错误消息的错误。
if name == "" {
return name, errors.New("empty name")
}
// 使用随机格式创建一条消息。
message := fmt.Sprintf("嗨,%v。欢迎!", name)
return message, nil
}
我们修改了 Hello 函数添加了返回值 error
,if
语句来检测输入的 name 是否是一个有效的值。如果为空则返回name 和 一个 错误 errors.New("empty name")
在 hello/hello.go
文件中,处理函数现在返回的错误 Hello
以及非错误值。
package main
import (
"example.com/greetings"
"fmt"
"log"
)
func main() {
message, err := greetings.Hello("demo007")
if err != nil {
log.Fatal(err)
}
fmt.Println(message)
使用标准库中的函数log package
输出错误信息。如果出现错误,可以使用 log
包的Fatal
来打印错误并停止程序。
在目录中的命令行中hello
,运行 hello.go 以确认代码有效
go run hello.go
Great to see you, demo007!
处理切片中的数据
我们将使用 Go 切片。切片类似于数组,只不过它的大小会随着添加和删除项目而动态变化。切片是 Go 最有用的类型之一。
我们将添加一个小切片来包含三条问候消息,然后让代码随机返回其中一条消息。
在 greetings/greetings.go 中,我们修改代码,如下所示。
package greetings
import (
"errors"
"fmt"
"math/rand"
)
func Hello(name string) (string, error) {
// 如果没有提供名称,则返回带有错误消息的错误。
if name == "" {
return name, errors.New("empty name")
}
// 使用随机格式创建一条消息。
message := fmt.Sprintf(randomFormat(), name)
return message, nil
}
func randomFormat() string {
formats := []string{
"Hi, %v. Welcome!",
"Great to see you, %v!",
"Hail, %v! Well met!",
}
return formats[rand.Intn(len(formats))]
}
1、添加一个randomFormat
函数,返回随机选择的问候消息格式。 randomFormat
以小写字母开头,使其只能被其自己的包中的代码访问(换句话说,它不会被导出)。
2、在 中randomFormat
,声明formats
具有三种消息格式的切片。声明切片时,您可以在括号中省略其大小,如下所示:[]string
。这告诉 Go 切片底层数组的大小可以动态更改。
3、使用该 math/rand
包生成一个随机数,用于从切片中选择一个项目。
4、在 中Hello
,调用该randomFormat
函数来获取您将返回的消息的格式,然后 name
一起使用该格式和值来创建消息。
5、像以前一样返回消息以及错误
在 hello/hello.go 中,我们修改代码,如下所示:
package main
import (
"example.com/greetings"
"fmt"
"log"
)
func main() {
message, err := greetings.Hello("demo007x")
// 如果返回了错误,请将其打印到控制台并退出程序。
if err != nil {
log.Fatal(err)
}
//如果没有返回错误,请将返回的消息打印到控制台。
fmt.Println(message)
}
我们运行代码看看输出的内容:
go run hello.go
Hail, demo007x! Well met!
go run hello.go
Great to see you, demo007x!
go run hello.go
Hi, demo007x. Welcome!
阿里云ECS11月销量王 99元/年 2核2G 3M固定带宽不限流量,新老同享,活动期间新购、续费同价,开发必备!
https://www.aliyun.com/lowcode/promotion/allinaliyun/99program?source=5176.11533457&userCode=c0ngnrad
测试 module
以上我们编写了基本的或者说是最简单的 golang module 的程序项目,如何确保我们的程序是无错误的或者是没有 bug 的?最好的办法就是添加 测试程序。
在开发过程中测试代码可能会暴露出在您进行更改时出现的错误。
Go 对单元测试的内置支持使我们可以更轻松地进行测试。具体来说,使用命名约定、Go 的testing
包和go test
命令,可以快速编写和执行测试。
-
1. 在greetings目录中,创建一个名为greetings_test.go的文件。以 _test.go 结尾的文件名告诉
go test
命令该文件包含测试函数。 -
2. 在greetings_test.go中,编写以下代码并保存文件:
package greetings
import (
"regexp"
"testing"
)
// TestHelloName 调用 greetings.Hello 并传入一个名称,检查返回值是否有效。
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 调用 greetings.Hello 并传入空字符串, 检查是否出现错误。
func TestHelloEmpty(t *testing.T) {
msg, err := Hello("")
if msg != "" || err == nil {
t.Fatalf(`Hello("") = %q, %v, want "", error`, msg, err)
}
} -
3. 创建两个测试函数来测试该
greetings.Hello
函数。测试函数名称的形式为,其中Name表示有关特定测试的信息。此外,测试函数将指向包的指针作为参数。可以使用此参数的方法来报告和记录测试。 -
4. 两个测试:
-
•
TestHelloName
调用该Hello
函数,传递一个name
值,该函数应该能够返回有效的响应消息。如果调用返回错误或意外响应消息(不包含您传入的名称),则可以使用参数t
的方法将消息打印到控制台并结束执行Fatalf。 -
•
TestHelloEmpty
Hello使用空字符串调用该函数。此测试目的在确认错误处理是否有效。如果调用返回非空字符串或没有错误,则可以使用t
参数的Fatalf
方法将消息打印到控制台并结束执行。 -
5. 在greetings目录下的命令行中,运行命令 go test 执行测试。
go test -v
=== RUN TestHelloName
--- PASS: TestHelloName (0.00s)
=== RUN TestHelloEmpty
--- PASS: TestHelloEmpty (0.00s)
PASS
ok example.com/greetings 0.007s通过测试输出的结果可以看到我们的程序全部都通过了测试。
发布module
以上我们编写了我们的第一个 go module, 并且编写了对应函数的测试程序,然后我们可以将我们的 module 程序提交到 github等系统。其他人就可以通过执行命令 go get example.com/greetings
将 greeting 包安装到自己的项目中。
总结:
golang module 给我们提供了灵活的本地开发并测试 module 的机制。我们通过修改 go.mod
文件,通过指令 replace example.com/greetings => ../greetings
来实现本地 module 的开发和测试的全部过程。
阿里云ECS11月销量王 99元/年 2核2G 3M固定带宽不限流量,新老同享,活动期间新购、续费同价,开发必备!
https://www.aliyun.com/lowcode/promotion/allinaliyun/99program?source=5176.11533457&userCode=c0ngnrad
阅读 99 知识派 喜欢此内容的人还喜欢 一文掌握 golang中 work与 module 的区别与联系 知识派 不看的原因
- 内容质量低
- 不看此公众号
- 内容质量低
- 不看此公众号
- 内容质量低
- 不看此公众号
人划线
标签:module,创建,Module,example,golang,greetings,go,com,hello From: https://www.cnblogs.com/cheyunhua/p/17930342.html