8.Go工程管理

1.工作区介绍

通过前面函数的学习,我们能够体会到函数的优势,就是可以将不同的功能放在不同的函数中实现,主函数(main( ))可以直接调用。这样结构非常的清晰,也非常方面代码的管理。如果我们把所有的代码都写在main( )函数中,会出现什么样的情况呢?

代码混乱,非常不容易管理。

但是现在我们面临了另外一个问题就是:我们所有自己定义的函数都写在了一个文件中。

如果我们做的项目代码量越来越多,那么该文件会变的非常臃肿,代码也会变得非常难管理。所以,我们在开发中,除了要定义函数,同时还要将代码放在不同的文件中。

例如:我们定义了一个UserInfo.go文件,里面包含了用户的添加函数,修改函数,删除函数等操作。

这就涉及到项目的工程管理也就是怎样对项目中的文件进行管理。

为了更好的管理项目中的文件,要求将文件都要放在相应的文件夹中。GO语言规定如下的文件夹如下:

  • (1)src目录:用于以代码包的形式组织并保存Go源码文件。(比如:.go .c .h .s等)
  • (2)pkg目录:用于存放经由go install命令构建安装后的代码包(包含Go库源码文件)的“.a”归档文件。
  • (3)bin目录:与pkg目录类似,在通过go install命令完成安装后,保存由Go命令源码文件生成的可执行文件。

以上目录称为工作区,工作区其实就是一个对应于特定工程的目录。

目录src用于包含所有的源代码,是Go命令行工具一个强制的规则,而pkg和bin则无需手动创建,如果必要Go命令行工具在构建过程中会自动创建这些目录。

2:创建同级目录

(2.1)创建src目录,在改目录下创建go源码文件

(2.1.1)在项目文件夹下新建src目录,如下图所示:


8.Go工程管理_java

image-20210505234545639

在F盘goProject目录下创建了src目录。

(2.1.2)在该目录下创建不同的go源码文件,如下图所示:

8.Go工程管理_linux_02


image-20210505235111375

在src目录下创建main.go文件和test.go文件(注意:这个两个文件是在同一个目录下面,都是在src目录下面)

  • main.go 文件下的代码如下所示:

8.Go工程管理_编程语言_03


image-20210505235307911

package main

import "fmt"

func main() {
fmt.Println("main")
}
  • test.go 文件下的代码如下所示:

8.Go工程管理_linux_04


image-20210505235307911

package main

import "fmt"

func Test() {
fmt.Println("Test")
}

现在已经完成两个文件代码的编写,接下来的问题是,我们怎样在main.go文件中的入口函数main( )中调用test.go文件中的Test( )函数呢?这就需要设置环境变量GOPATH属性。如果要完成不同文件中函数的调用,必须设置GOPATH,否则,即使文件处于同一工作目录(工作区)下,也是无法完成调用的。

(2.2)GOPATH设置

GOPATH设置的具体步骤如下:

8.Go工程管理_java_05


image-20210505235857369

8.Go工程管理_java_06


image-20210505235307911

配置完成后,可以执行​​go env​​ 测试一下是否配置成功

8.Go工程管理_python_07


image-20210505235307911

(2.3) 在main.go文件中完成对test.go文件中函数的调用

8.Go工程管理_java_08


image-20210506000804338

原因:

在 run/debug configurations 设置中run kind(运行类型)选择了File(文件)类型,在运行go程序时,只会编译指定文件(main.go),其他文件不会编译,所以main.go不能调用其他文件的函数,导致报错。如图所示:

8.Go工程管理_linux_09


image-20210506000940196

使用命令的方式执行需要编译的所有go文件:

上面出错的原因是因为只编译了 main.go 文件,而没有同时编译 test.go 文件,导致 Test() 函数提示未定义。我们可以使用命令行首先来编译所有 go 文件,如下:

8.Go工程管理_编程语言_10


image-20210506002003652

配置 go.mod file :

新版本的 go 在执行之前还要生成 mod 文件,如下:

8.Go工程管理_python_11


image-20210506002813507

如果没有生成,则会报错。下面我们来生成一下,初始化模块。

执行 ​​go mod init main/main​

8.Go工程管理_编程语言_12


image-20210505235307911

解决方案:

还需要将执行的编译改为编译该src目录下的所有 go 文件,如下:

在run->edit Configuration界面设置run kind(运行类型)为Directory,并且指定需要编译的源码路径 以及 二进制文件的输出路径。

如图所示:

8.Go工程管理_python_13


image-20210506003228851

最后编译执行测试如下:

8.Go工程管理_java_14


image-20210506003339994

注意:同一个目录下不能定义不同的package。

3:创建不同级目录

在上一小节中,将不同的go源代码文件都放在了同一个目录下面,如果将go源代码文件放在不同的目录下面应该怎样进行处理呢?

具体的步骤如下:

(3.1)新建项目目录,如下图所示

8.Go工程管理_java_15


image-20210505235307911

  1. 在“CmsProject”目录下面,创建src目录,在src目录下面创建如下文件目录与文件.

8.Go工程管理_java_16


image-20210505235307911

8.Go工程管理_go_17


image-20210505235307911

  1. main.go 定义的是入口函数main( ).

  1. userinfo 文件夹下定义的是user.go文件。

8.Go工程管理_python_18


image-20210505235307911

user.go 文件中的代码如下:

8.Go工程管理_python_19


image-20210505235307911

package userinfo

import "fmt"

func Add() {
fmt.Println("添加用户信息")
}
  1. main.go 文件中的代码如下:

8.Go工程管理_java_20


image-20210505235307911

package main

import "fmt"
import "userinfo"

func main() {
fmt.Println("main")
userinfo.Add()
}

通过以上两个文件中的代码,可以总结出如下几点:

第一:不同目录,包名不一致(自定义包)。

第二:main.go中调用user.go中的方法时,一定要导包,并且调用的方式是:包名.函数名 的方式

(3.2)要想运行以上代码,还需要设置GOPATH环境变量的值,可以通过以下方法进行快速设置:

8.Go工程管理_java_21


image-20210505235307911

(3.3)配置编译命令

8.Go工程管理_python_22


image-20210506011417333

(3.4)禁用 GO111MODULE

按照上面配置完毕之后,进行执行则会报错,如下:

8.Go工程管理_java_23


image-20210506011506751

原因分析:

因为它是报需要寻找GOROOT下的包,应该是路径问题,或者环境设置问题,输入go env检查了一下环境,发现GO111MODULE=on,编译器没有去gopath下找包。gomod 和 gopath 两个包管理方案,并且相互不兼容,在 gopath 查找包,按照 goroot 和多 gopath 目录下 src/xxx 依次查找。在 gomod 下查找包,解析 go.mod 文件查找包,mod 包名就是包的前缀,里面的目录就后续路径了。在 gomod 模式下,查找包就不会去 gopath 查找,只是 gomod 包缓存在 gopath/pkg/mod 里面。

解决方案:

设置GO111MODULE为off即可。

go env -w GO111MODULE=off

设置完毕之后,再次测试执行:

8.Go工程管理_python_24


image-20210505235307911

(3.5)注意:user.go文件中的函数名首字母必须大写,如果改成小写在main.go中无法进行调用

这种不同级目录应用,在以后的项目开发中使用频率非常高。例如:上面我们的案例中,可以将用户管理的操作放在userinfo目录下,商品管理模块可以再定义一个目录,例如:product.如下图所示:

8.Go工程管理_java_25


image-20210505235307911

product.go中的代码如下:

package product

import "fmt"

func AddProduct() {
fmt.Println("添加商品信息")
}

main.go中的代码如下:

8.Go工程管理_python_26


image-20210505235307911

package main

import (
"fmt"
"product" // 导入product包
"userinfo" // 导入userinfo包
)

func main() {
fmt.Println("main")
userinfo.Add()
product.AddProduct() // 调用商品添加
}

(3.6)关于包的问题

我们已经讲解了关于包的概念,包就是一个标识,标志着代码是来自哪儿,对代码进行管理。

所以,在main( )函数中要使用相应的函数,必须进行导包,然后根据包名去调用相应的函数。

通过上面的代码,我们也能够体会出“包”的优势,就是可以在userinfo包中定义名叫Add( )方法,在product包中也可以定义Add( )方法,但是在main( )函数中进行调用时,通过包名进行调用,就可以很清楚Add( )方法来自哪个包,不会造成混乱,和名称的冲突。并且相关的功能代码,放在一个包中,可以很好的被复用。

例如:可以在userinfo包中使用product,如下图所示:

8.Go工程管理_python_27


image-20210505235307911

但是我们创建的的自定义包最好放在GOPATH的src目录下,在Go语言中,代码包中的源码文件名可以是任意的。但是,这些任意名称的源码文件都必须以包声明语句作为文件中的第一行,每个包都对应一个独立的名字空间。

包中成员以名称⾸字母⼤⼩写决定访问权限:

  • public: ⾸字母⼤写,可被包外访问
  • private: ⾸字母⼩写,仅包内成员可以访问

注意:同一个目录下不能定义不同的package。

(3.7)导包的问题

在上面的案例中,要使用包,必须要进行导入,可以通过关键字进行import进行导入,它会告诉编译器你想引用该包内的代码。

如果导入的是标准库中的包(GO语言自带,例如:”fmt”包)会在安装 Go 的位置找到。Go 开发者创建的包会在 GOPATH 环境变量指定的目录里查找。所以,import关键字的作用就是查找包所在的位置。

如果编译器查遍 GOPATH 也没有找到要导入的包,那么在试图对程序执行 run 或者 build 的时候就会出错。

注意:如果导入包之后,未调用其中的函数或者类型将会报出编译错误。

8.Go工程管理_java_28


img

我们常规的导包方式是用import关键子一个个导入。

例如:

import "fmt"
import "product"
import "userinfo"

表示导入三个包,有GO语言自带的包,也有我们自定义的包。但是,这种写法可能比较麻烦,所以为了简化也可以采用如下的方式进行导包:

import (
"fmt"
"product" // 导入product包
"userinfo" // 导入userinfo包
)

这种方式,使用的频率是非常高的。