详解如何在golang项目开发中创建自己的Module
作者:demo007x
为什么要创建一个 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
- 创建模块:编写一个小模块,其中包含可以从另一个模块调用的函数。
- 从另一个模块调用您的代码:导入并使用您的新模块。
- 返回并处理错误: 添加简单的错误处理。
- 处理切片中的数据(Go 的动态大小数组)。
- 添加测试程序:使用 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
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
通过调用包的 函数 来获取输出信息。
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!
测试 module
以上我们编写了基本的或者说是最简单的 golang module 的程序项目,如何确保我们的程序是无错误的或者是没有 bug 的?最好的办法就是添加 测试程序。
在开发过程中测试代码可能会暴露出在您进行更改时出现的错误。
Go 对单元测试的内置支持使我们可以更轻松地进行测试。具体来说,使用命名约定、Go 的testing
包和go test
命令,可以快速编写和执行测试。
在greetings目录中,创建一个名为greetings_test.go的文件。
以 _test.go 结尾的文件名告诉
go test
命令该文件包含测试函数。在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) } }
创建两个测试函数来测试该
greetings.Hello
函数。测试函数名称的形式为,其中Name表示有关特定测试的信息。此外,测试函数将指向包的指针作为参数。可以使用此参数的方法来报告和记录测试。两个测试:
TestHelloName
调用该Hello
函数,传递一个name
值,该函数应该能够返回有效的响应消息。如果调用返回错误或意外响应消息(不包含您传入的名称),则可以使用参数t
的方法将消息打印到控制台并结束执行Fatalf。TestHelloEmpty
Hello使用空字符串调用该函数。此测试目的在确认错误处理是否有效。如果调用返回非空字符串或没有错误,则可以使用t
参数的Fatalf
方法将消息打印到控制台并结束执行。
在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 的开发和测试的全部过程。
以上就是详解golang项目开发如何创建自己的Module的详细内容,更多关于golang创建Module的资料请关注脚本之家其它相关文章!