简介
上一篇 【说人话的 Golang 小白入门】EP01 - 问候世界? Hello World! 刚完成 Hello World,想必小伙伴都想赶紧把各种库用起来开发自己想要的程序!
但是 但是 但是 等一下 等一下 等一下。。。 先來说说怎么初始化 Module 吧!
(你问 Module 是什么?先动手再说!)
动手环节
PART 1: Module 初始化
先创建一个文件夹 hello-go,后续教程的代码都将整理在这个文件夹底下
- 以下会以 hello-go/{文件名} 示意文件所在位置
➜ mkdir hello-go
然后指定你的 Module 名称,以初始化 go.mod 文件
- 然而!Module 命名正是本章最大的挑战!我第一次的时候在这卡了有半个小时!
- 可以先参考一下一些公开库的命名吧
- 原生库
- 单词,如 fmt,math,strings
- gopkg.in 上的项目名一般是下面两种
- CASE 1: gopkg.in/{仓库名}.{版本号}, 如 gopkg.in/yaml.v2
- CASE 2: gopkg.in/{用户名}/{仓库名}.{版本号}, 如 gopkg.in/go-playground/validator.v9
- github.com
- github.com/{用户名}/{仓库名},如 github.com/aws/aws-sdk-go
- 原生库
- 可以看出来前缀的作用就是为了区分不同用户/机构/来源的开源库,所以
- 本地自用的话按 {个人代号}/{项目名} 命名就好
- 准备上传到类似 github.com 的网站公开的话就按 github.com/{用户名}/{仓库名} 命名
➜ go mod init kabuski/hello-go
go: creating new go.mod: module kabuski/hello-go
查看创建的 go.mod,可以看到指定的 Module 名称和当前使用的 Go 版本
➜ cat go.mod
module hello-go
go 1.16
这样 Module 就初始化好喇!
PART 2: Package 定义和引用
这个时候我们把之前 Hello World 如下拆解
hello-go
│
└─ hello
│ └─ hello.go
│
└─ main.go
└─ go.mod
// hello/hello.go
package hello
import "fmt"
func SayHello(){
fmt.Println("Hahahaha Hello World")
fmt.Printf("Welcome to %s's GoLang Tutorial~~", "KabuSki")
}
// main.go
package main
import "kabuski/hello-go/hello"
func main(){
hello.SayHello()
}
这样代码就准备好了~还是一样的命令运行程序试试
➜ go run main.go
Hahahaha Hello World !!!
Welcome to KabuSki's GoLang Tutorial ~~!!!
哒哒!拆腾了一轮,没有一点区別!(你踏马在逗我 ?
代码解读
Part 1: 上面都干了什么?
首先,我们把打印文字的函数搬到了 main.go 以外的文件 hello/hello.go 中的 func SayHello
那 main.go 裡怎么引出这个函数呢?正是下面这一行
import "kabuski/hello-go/hello"
机智的你一定看出来了,前缀 "kabuski/hello-go" 正是前面 mod init 指定的 Module 名称
那后缀的 hello 呢?是文件夾名称?还是 hello.go 中首行声名的 Package 名称?
3..
2..
1..
0.. 哒哒!答案是文件夹名称!
在 Golang 中,Package 的引用是由 {Module}/{路径} 指定的。
相对地,想要引用某段代码文件中声名的函数或变量的话,它就必须被包装为一个 Module 中的一个 Package 的成员。只要你的项目包含不只一个代码文件,你都需要将他作为 Module 初始化 。
Part 2: 引用 Package 之后怎么调用它的成员?
在 main.go 中能看到,我們以下面这一行调用了 package hello 的函数 func SayHello
hello.SayHello()
注意这裡 SayHello 是大写字母开头的,只有大写字母开头的函数、成员等才可以被调用,关于这一点会在后续章节说明。
那么问题来了,这裡的 hello 是文件夾名称还是 hello.go 中首行声名的 Package 名称?
3..
2..
1..
0.. 哒哒!答案是 Package 名称!
这时候你应该猜到了,文件夹 和 Package 是一对一的关系,一个文件夹下的代码文件都属于同一个 Package,一个文件夹下的代码文件首行声明的 package 也必须相同,否则会报错。
但是文件夹下的文件夹和这个文件夹不属于同一个 Package (你搁这套娃呢?
还是举个具体的栗子吧,以下面这个为例
hello-go
│
└─ hello
│ └─ english
│ │ └─ hello_in_english.go
│ │
│ └─ hello.go
│
└─ main.go
└─ go.mod
这裡 hello/english/hello_in_english.go 可以声明为 package english 而不是 package hello。但无论你在 hello_in_english.go 中声明的 package 名是不是 hello,如果你只 import "kabuski/hello-go/hello" 的话,那都不能直接引用 hello_in_english.go 裡的成员。
那我可以直接 import "kabuski/hello-go/hello/english" 吗?
可以!
那为什么要套这么多层文件夹?
这个单纯是为了让开发人员能更好地从逻辑上组织代码,但怎么组织一个项目的代码文件正正体现了开发人员的水平。
那 Package 名称可以跟文件夹名称不一样吗?
可以!
如果这是自己用的代码,那没关系,除了给自己添堵以外没什么毛病。
但如果你是团队合作开发的话,答应我別这么干,会出人命。
结语
本章主要解決了在 Golang 语言裡,如何在一个文件中引用另一个文件定义的函数,引入了 Module 和 Package 的概念。
更深入的说明还是留到下一章吧,下一章见~