menu.go第二个版本-使用开源社区的典型写法

1. 项目结构的初始化

本次实验我们使用模块化的思想完成 menu 程序的第二版。

在 Go 语言中要想编写额外的包来供 main 函数调用,我们首先要初始化 .mod 文件,在命令行执行如下命令:

go mod init gitee.com/phony36/menu

执行完成后,在 menu 文件夹中会出现一个名为 go.mod 的文件,其内容如下: 

menu.go第二个版本-使用开源社区的典型写法_第1张图片

然后在menu 文件夹下新建 linklist 文件夹,并在其中新建 datanode.go 和 linklist.go 文件。完成后,现在的项目结构应该像下面这样:

menu.go第二个版本-使用开源社区的典型写法_第2张图片

2. 数据结构的定义

        我们在 datanode.go 文件中定义结构体 DataNode ,它包括命令名称、命令描述、命令对应的 handler 函数和指向下一个 DataNode 结点的指针。注意所有的变量名首字母都要大写,这样我们才能在 linklist 包外访问该 DataNode 和它的成员。datanode.go 文件的代码如下:

package linklist

type DataNode struct {
	Cmd string
	Desc string
	Handler func() int
	Next *DataNode
}

 3. 实现业务处理函数 

        在 linklist.go 中实现 FindCmd 和 ShowAllCmd 函数,FindCmd 函数用于找到命令名称为 cmd 的 DataNode 结点并返回指向它的指针;ShowAllCmd 函数遍历整个 DataNode 链表并打印命令名字和命令描述。同样的,这两个函数首字母也要大写才能供外部库调用。linklist.go 文件的代码如下:

package linklist

import "fmt"

func FindCmd(head *DataNode, cmd string) *DataNode {
	if head == nil || cmd == "" {
		return nil
	}
	var p *DataNode = head
	for p != nil {
		if p.Cmd == cmd {
			return p
		}
		p = p.Next
	}
	return nil
}

func ShowAllCmd(head *DataNode) int {
	fmt.Println("Menu List:")
	var p *DataNode = head
	for p != nil {
		fmt.Printf("%-7s - %s\n", p.Cmd, p.Desc)
		// fmt.Println("%-7s - %s\n", p.Cmd, p.Desc)
		p = p.Next
	}
	return 0
}

4. 实现主函数 

现在开始编写 main.go 文件。

上述编写的两个文件中的函数和数据结构都在包 linklist 下,为了在 main.go 文件中调用它们,我们要导入 linklist 包:

package main
 
import (
    "fmt"
    "os"
    "gitee.com/phony36/menu/linklist"
)

 紧接着定义相关的常量,避免使用幻数 (magic number) 。

const (
    CMD_MAX_LEN = 128
    DESC_LEN = 1024
    CMD_NUM = 10
)

然后定义我们要实现的命令及其相关参数,这些命令组织成一个 DataNode 的链表,并用一个名为 head 的变量指向这个链表的头结点:

var head *linklist.DataNode = &linklist.DataNode {
    Cmd: "help",
    Desc: "this is help command",
    Handler: nil,
    Next: &linklist.DataNode {
        Cmd: "version",
        Desc: "menu program v1.0",
        Handler: nil,
        Next: &linklist.DataNode {
            Cmd: "quit",
            Desc: "exit the program",
            Handler: quit,
            Next: nil,
        },
    },
}

我们定义了 help 、version 和 quit 三条命令,其中 help 和 quit 都有对应的 handler 函数,它们的实现如下: 

var help = func() int {
    linklist.ShowAllCmd(head)
    return 0
}

var quit = func() int {
    fmt.Println("Bye.")
    os.Exit(0)
    return 0
}

 最后在 main 函数中实现接收输入和进行对应处理的逻辑。最终 menu.go 文件应该像下面这样:

package main
 
import (
    "fmt"
    "os"

    "gitee.com/phony36/menu/linklist"
)

const (
    CMD_MAX_LEN = 128
    DESC_LEN = 1024
    CMD_NUM = 10
)

var head *linklist.DataNode = &linklist.DataNode {
    Cmd: "help",
    Desc: "this is help command",
    Handler: nil,
    Next: &linklist.DataNode {
        Cmd: "version",
        Desc: "menu program v1.0",
        Handler: nil,
        Next: &linklist.DataNode {
            Cmd: "quit",
            Desc: "exit the program",
            Handler: quit,
            Next: nil,
        },
    },
}

var help = func() int {
    linklist.ShowAllCmd(head)
    return 0
}

var quit = func() int {
    fmt.Println("Bye.")
    os.Exit(0)
    return 0
}

func main() {
    for {
        cmd := make([]byte, CMD_MAX_LEN)
        fmt.Println(">>> Input a command: ")
        fmt.Scanln(&cmd)
        p := linklist.FindCmd(head, string(cmd))
        if p == nil {
            fmt.Println("This is a wrong cmd!")
            continue
        }
        // fmt.Printf("%s - %s\n", p.Cmd, p.Desc)
        fmt.Printf("%s - %s\n", p.Cmd, p.Desc)
        if p.Cmd == "help" {
            help()
        }
        if p.Handler != nil {
            p.Handler()
        }
    }
}

遇到的问题 

在执行menu.go时遇到了以上问题,通过查询相关博客发现自己是将$GOPATH指向了go.mod所在目录,此时应该在命令行中取消这一环境变量设置:

$ unset GOPATH

为什么将$GOPATH指向了go.mod所在目录就不能正常运行程序了呢?

原因如下:如果设置了环境变量$GOPATH,Go 将默认在$GOPATH/pkg/中下载和导入依赖库,而go.mod文件则是将当前工作目录作为项目根目录,从./pkg/中下载和导入依赖库。因此,当$GOPATH指向go.mod所在目录时,就会产生矛盾。

所以解决思路也是显而易见的:应该在执行go mod init命令后将$GOPATH也指向了项目目录,所以应该首先选择通过命令行unset GOPATH

最佳实践

从Go1.13开始,使用Go Modules管理Go项目,放弃$GOPATH

  1. 创建并进入项目目录;
  2. 开启Go Modules:export GO111MODULE=on
  3. 项目初始化:go mod init

解决了上述问题后,程序得以成功运行。

menu.go第二个版本-使用开源社区的典型写法_第3张图片

5. 推送到远端仓库

执行下面的命令将其推送到远端仓库:

menu.go第二个版本-使用开源社区的典型写法_第4张图片

 在gitee中查看:

menu.go第二个版本-使用开源社区的典型写法_第5张图片

你可能感兴趣的:(高级软件工程,linux,运维,服务器)