Go 语言学习:创建一个 Go 模块

原文 Tutorial: Create a Go module

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

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

Go 代码被组织成包,包被组织成模块。你使用的包的模块指定了 Go 运行代码所需要的上下文,它包括编写代码的Go版本及其所需的其他模块集合。

在模块中添加或改进功能时,你会发布模块的新版本。使用你模块的开发人员可以导入最新的版本进行测试,以便在生产环境中使用。

步骤如下:

  1. 在 "gogo"目录中【/root/gogo】,创建一个新目录"greetings ",这个目录包含模块的源码。
    mkdir greetings 
    cd greetings 
    
  2. 使用 go mod init 命令创建一个 go.mod文件。
    root@ubuntu:~/gogo/greetings# go mod init example.com/greetings
    go: creating new go.mod: module example.com/greetings
    
    “example.com/greetings” 是模块的路径。在生产环境中,路径将会是你要下载模块的 URL。
  3. 新建文件" greetings.go",内容如下
    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
    }
    
    在这段代码中:
    • 第一行声明了代码所属的包为"greetings"。
    • 实现了一个名为"Hello"的函数,根据输入的名字,返回特定格式的问候语。

从另一个模块中使用你的代码

  1. 在 “gogo"目录中,创建一个新目录"hello”【与"greetings"目录同级】。
    mkdir hello
    cd hello
    
  2. 创建一个新文件"hello.go",内容如下:
    package main
    
    import (
        "fmt"
    
        "example.com/greetings"
    )
    
    func main() {
        // Get a greeting message and print it.
        message := greetings.Hello("Gladys")
        fmt.Println(message)
    }
    
    在这段代码中:
    • 第一行声明了一个名为"main"的包。在 Go 中,作为一个程序执行的代码必须包含在 main 包中。
    • 第六行导入了名为 "example.com/greetings "的包。
    • 在main函数中使用了 greetings 包中的 Hello函数,返回一个问候语。
  3. 为这个 hello 包创建一个新的模块。
    root@ubuntu:~/gogo/hello# go mod init hello
    go: creating new go.mod: module hello
    
  4. 修改go.mod文件,来使用未发布的 greetings 模块。
    1. 在文件最后添加一行“replace example.com/greetings => …/greetings”,修改后内容如下:

      module hello
      
      go 1.13
      
      replace example.com/greetings => ../greetings
      
    2. 执行go build命令。命令执行后,go.mod文件内容被修改了,添加了一行内容:

      module hello
      
      go 1.13
      
      replace example.com/greetings => ../greetings
      
      require example.com/greetings v0.0.0-00010101000000-000000000000
      

      require 指令指明了 hello 模块依赖 example.com/greetings 模块,而 replace 指令指明了模块的位置。【因为我们的模块未发布】

      在引用一个已经发布的模块时,require 指令在模块后会包含一个版本号, replace 指令会被忽略。如下所示:

      require example.com/greetings v1.1.0
      
    3. 执行 hello 程序。

      root@ubuntu:~/gogo/hello# ./hello 
      Hi, Gladys. Welcome!
      

添加测试用例

原文中返回并处理一个错误、返回一个随机的问候语、为多人返回问候语,这几节就不提了。看下原文,将代码粘贴下来跑跑,就可以了,这些代码中提到的返回并处理错误、字典、切片等知识点后续还会提到的。

模块代码写完后,为了确保功能和我们预期的相同,我们需要写一些测试用例。Go 原生就对单元测试支持得很好。使用 "testing"包和 go test命令,我们能快速写出并执行测试。

下面的步骤是我们如何为"greetings"模块添加测试用例:

  1. 在"greetings" 目录,新建一个名为"greetings_test.go"的文件。文件的名字以"_test.go"结尾告诉 go test命令文件中包含测试函数。

  2. 文件内容如下:

    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)
        }
    }
    

    在这段代码中,实现了两个测试函数来测试 greetings.Hello 函数。测试函数的名字必须是"TestName"的形式,"Name"是测试函数的名称。测试函数的参数是一个指向 testing.T 的指针,可以使用这个参数的方法来报告并输出测试日志。

    第一个测试函数,测试给 Hello 函数一个非空字符串参数,是否返回的问候语中包含传入的字符串。如果不包含,或者函数返回错误,那么就调用 Fatalf 方法输出提示信息,并终止测试。

    第二个测试函数,测试给 Hello 函数一个空字符串参数,是否也返回空字符串,并返回一个错误。

    接下来执行go test命令运行测试函数。加 -v 参数会显示更详细的测试信息。

    $ 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 run命令,这个命令并不会产生二进制可执行程序,要产生可执行程序文件就需要使用go install命令。

在安装之前,我们最好设置下安装目录

go env -w GOBIN=/home/liu/go/bin

在 hello 目录执行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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用Go语言编写图书管理系统的基本步骤: 1. 安装Go语言环境和相关依赖库,例如gin框架和gorm库。 2. 创建一个新的Go模块,并在其中创建一个main.go文件。 3. 在main.go文件中导入所需的依赖库,并创建一个路由器对象。 4. 创建一个Book结构体,用于表示图书信息,并使用gorm库创建一个books表。 5. 在路由器对象中添加处理HTTP请求的函数,例如添加图书、删除图书、更新图书和获取所有图书等。 6. 在处理HTTP请求的函数中,使用gorm库对books表进行增删改查操作。 7. 运行程序并测试各个HTTP请求处理函数是否正常工作。 以下是一个简单的示例代码,用于演示如何使用Go语言编写图书管理系统: ```go package main import ( "net/http" "github.com/gin-gonic/gin" "gorm.io/driver/mysql" "gorm.io/gorm" ) type Book struct { ID uint `json:"id" gorm:"primaryKey"` Title string `json:"title"` Author string `json:"author"` } func main() { // 连接MySQL数据库 dsn := "root:password@tcp(127.0.0.1:3306)/library?charset=utf8mb4&parseTime=True&loc=Local" db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { panic("failed to connect database") } // 自动迁移数据库表结构 db.AutoMigrate(&Book{}) // 创建路由器对象 router := gin.Default() // 添加图书 router.POST("/books", func(c *gin.Context) { var book Book if err := c.ShouldBindJSON(&book); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } db.Create(&book) c.JSON(http.StatusOK, book) }) // 删除图书 router.DELETE("/books/:id", func(c *gin.Context) { var book Book if err := db.First(&book, c.Param("id")).Error; err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Record not found!"}) return } db.Delete(&book) c.JSON(http.StatusOK, gin.H{"message": "Book deleted successfully!"}) }) // 更新图书 router.PUT("/books/:id", func(c *gin.Context) { var book Book if err := db.First(&book, c.Param("id")).Error; err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Record not found!"}) return } if err := c.ShouldBindJSON(&book); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } db.Save(&book) c.JSON(http.StatusOK, book) }) // 获取所有图书 router.GET("/books", func(c *gin.Context) { var books []Book db.Find(&books) c.JSON(http.StatusOK, books) }) // 运行HTTP服务器 router.Run(":8080") } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值