粉丝9获赞31

我们开发一个完整的项目,肯定会写很多的购文件,这些购文件之间本身会互性的依赖,同时我们除了会依赖购标准库之外,肯定也会依赖很多第三方库。 那么今天就给大家演示一下如何去管理这些复杂的依赖,如何用构源去构建一个五杂的项目, 好给大家看一个 demo, 那 这是我写的一个简单的系统,那里面呢,包含 两个文件加两个 e g 目录和几个文件,大家看一下啊。首先我的 e g 目录叫做 go on in six arcs 啊,这是我项目的根目录, 在这个根部下面呢,有两个文件,一个是命文件,一个是 youtube 文件,那命文件下面有一个 d 点勾, youtube 下面呢,有一个 a 点勾,同时还有一个 mass 这样的个子目录,那 mass 目录下面呢,又有 c 点勾和 d 点勾, 同时在我的项目根目下面还有一个 go 点 mode 和 go 点 sum 啊,这是我的目录结构。那这些代码之间是怎么个互相依赖呢? 在 max 这层目录下面, c 点 go, 它会去调用 d 点 go 里面的内容, 而我的程序的入口命函数在 d 点 go 这个文件里面, d 点 go 呢,它也会去调用 a 点 go, c 点 go 同时会去调用用一个第三方的库,这是它们之间的依赖关系。 好记住这个依赖关系,然后我们就记用到代码部分,教大家该如何去写代码, ok, 这是我最终写好的代码。首先呢告诉大家,我们要去构建一个构项目的话,第一步是要建立一个构点 mode 啊,构点 mode 这样的一个文件 怎么构建呢?我先把这个文件删掉啊,一步一步教大家怎么构建,先删除构点 mode, 我 也把构点 sum 删掉好,删掉了,然后打开我们的终端 啊,我们来到我们项目的根目录, go learn in six hours, 下面我们通过一个命令, go mode unit。 哎,通过这个命令呢,来创来初步化我们的项目。后面是跟上你的这个叫做 model 的 名称,这个名称可以随便取,我就简单取一个 g 六,随便取啊, 好,回车。好,那么执行完这行命令之后呢,它会自动地帮我们去生成哎, go 点 mode 这样一个文件,我们点开看看里面是什么内容。 第一行呢,是我们模块的名称, g 六是我刚刚指定的,下面是一个 go 的 版本,一点二零,好,这是最开始的状态。 ok, 将来我们的所有依赖都会依赖于这个模块的名称啊。 好,我们再来把里面的内容代码简单看一看。 nice, 下面呢,那么 b 点 go 里面,它有一个 package, 它叫 mess 啊,里面定了一个函数 sub, 它是计算两个整数之差。 看一下 c 点 go, c 点 go, 下面呢,有一个函数叫做 add 啊,它传的是三个参数 啊,它是 a 加上 a, b 减去 c。 那 注意这个地方,它调用了这个 sub 函数,就是我们刚刚在 b 点勾里面定义好的这个 sub 函数啊,所以说,你看,在 c 点勾里面,它调用了 b 点勾里面的这个函数。 在这里面呢,我们就要强调一下, b c 两个文件,它们都是在 mems 这个目录下面的,那么呢,同一个目录下面的所有的购文件,它们的 package 名称必须保持一致,你看,这边叫 mems, 这边也叫 mysis, 这个包的名称跟目录的名称没有什么必然关系,它们可以不同,但是同一个目录下面所有的购文件,它的 package 名称本身必须保持一致。 这是第一点。第二点,那同一个目录下面这些个购文件,它们肯定属于同一个 package, 同一个 package 内部,它们可以自由地、随意地互相调用。比方说我在 c 点勾里面直接调用 b 点勾里面的 sub 函数,对吧?我就可以直接用。这跟什么是等价的呢?就好比说,我把这个函数从 b 点勾里面 拷贝到 c 点勾里面,然后把 b 点勾这个文件删掉,跟这种情况完全是等价的。也就是说,大家记住 同一个目,假如说我把同一个目录下面所有的勾文件里面所有的代码合并到同一个文件里面,嗯,的代码是不用做任何修改的。 ok, 它们是完全等价的。 好,我先把它改回去。那么既然可以合到一个文件里面,为什么还要分成多个文件呢?主要是为了方便。 呃,我们的可读性啊,因为你根据文件名,你大概能够猜到不同的文件里面,它们放的一些函数功能是经过人人为的划分分类之后的,方便你查找 好。然后还要注意点,我们看到 sub 这个函数呢,它是以小写开头的,而我们的这个 ad 函数呢,是以大写开头的。那么大写开头的函数, 它在其他的 package 里面是可以调用的,是可见的。而小写开头的函数仅在本包内可见,除了这个包,别人是看不到你的,是无法调用它的。 这个不光是对于函数,包括对于变量也是一样的。比方说我在 a 点 go 这个地方定义了一个全句变量, name 呢,它是以大写开头的, 那么我在 mean 这个包下面,我就可以去使用 name 这个变量。但如果说 name 是 小巧巧的,那么除了 youtube 这个包是看不到 name 这个变量的,无法调用。 好,那我们的 a 点勾里面呢?除了定义一个 name 之外,也定义了一个,同样,它也定义了一个 add 函数, 那它是直接求两个数之和。在 c 点勾里面是 a 加上 b 减 c 都叫 i 的, 但它们所出的包不一样。 ok, 最终我们来到我们的真正的程序入口 d 点勾,那么作为程序的入口,它必须有一个命函数,而且仅有一个命函数,而且它的包名必须叫命 啊,这个 package 名称必须叫命。那么对于目录名称啊,没有任何要求,目录名称你可以不叫命,因为刚才我们也说了,目录名称和这个 package 名称没有必然关系,它们可以完全不一样。 那在这样一个目录下面,可以有多个文件,没关系,但是在一个目录下面,只能出现一个命函数,因为命函数是程序的入口,是程序的唯一入口。 好,我们把代码全部删掉,我们一边写一边跟大家讲。对于这样一个复杂的依赖关系,我代码层面应该怎么写? 首先呢,我先引入一个 smt 啊,这个是我们肯定会用到的一个包。 好,那么在 ut 下面, a 点 go 里面有一个变量。哎,我在我的 d 点 go 里面,我想直接使用这个变量, 我想直接使用这个变量,这样写行不行呢?不行啊,你得把 package 名称给带上。那么我们知道 a 点 go 它的 package 名称叫 ut 啊,你要把 ut 给带上, 是 youtube 点 name。 注意啊,这个 youtube 是 这里面的 package 名称,它跟 a 点 go 的 目录名称可没任何关系。 好,那你除了要在 name 前面加这样一个 youtube 点之外,还需要在 import 里面把它 import 进来。好,那 import 这边该怎么写呢?首先 写上的是我们 go 点 mod 里面这个 mod 名称 g 六好,首先应该把 g 六写上, g 六好,下一集呢,就是我们的这个 a 点 go 它所的目录名称, 它在 youtube 这个目录下面啊, youtube 这一集目录下面, 这里面注意一下,我这边的这个 foo 是 文件结构里面的目录名称,而这个 foo 是 go 代码里面的那个 package 名称,只不过此时它们俩刚好相同而已。 ok, 我 们把代码运行一下,看能不能输出 name。 go run。 好, 这个时候我们的主函数是在我们的 d 点 go 里面,那么 run 的 时候应该怎么指定呢?其实你只需要指定 我们的命函数所谓的目录就可以了,那目录呢?就是命,它只需要指定到目录这层就可以了,因为它会去这个目录下扫描所有的 go 文件,找到命函数 运行。 ok, 它会把 name 对 应的值打印出来。好,我们再来演示一个,这只是 name。 然后如果我们还想调用它的函数, 那 a 点 go 里面有一个 i 的 函数嘛?我们想调用一下,跟调用这个变量是一样的啊,调函数跟调变量是一样的, 比方说三加四等于七,也是需要在这个函数名称前面加上一个 u 点勾,也是需要把它给 import 进来 运行一下。 好,七啊,三加四等于七。 ok, 然后我们来一个稍微复杂一点的例子,现在,哎,我想去调用 这个 b 点里面的这个 sub 点勾,那是不行的,因为挎包这种小写的是不允许调用的,那我们只能调用这个大写的 i 的。 好,我们调一下 大写的 i 的, 只不过这个 add 呢,它是需要传三个整数过来,我们就传一二三。好,那么呢, add 的 前面是你的那个 package 名称,我们说 c 点 go, 它的 package 名称叫 masses, 好, 叫做 masses。 同时呢,在 import 里面,你需要把这个 masses 给引入进来。好,这个 import 怎么写 还是一样的啊?第一级目录是我们 go 点 mode 里面这个 model 的 名称 g 六, g 六, 然后我们说 c 点 go, 它位于 youtube 下面的 mass 这样一个字幕下面,所以呢,你需要先写上 youtube, 再写上 mass。 ok, 这里这个地方大家看的就比较明显了,因为 后面的都是你文件系统的目录名称,我的目录叫 mess, 但是我的 package 多了个 s, 叫 message, 是 吧?好,我们把代码编写一下。 好,因为这个 i 的 它的逻辑是什么?它的逻辑是 a 加上 b 减 c, 那 所以就是一加上二减三,一加负一等于零,对吧?那刚才我们看到它的运行结果也确实是零。 好,那这个地方呢,其实我们可以给这个 import 起个别名啊,比方说我们随便给它起个别名啊,叫做啦啦吧啦啦。好,有了别名之后,那么 我在调用它里面的函数或者边的时候呢,就可以改成叫啦啦。 好,就这个地方是起了个别名,拉拉啊,这个地方就改成了拉拉。好,我们再运行一下。 好,跟刚才结果一样。那同样道理,那我这边如果改成叫 mess 的 话,这边是不是也可以叫 mess, 对吧?我们运行一下 run, 哎,把这个 run 好, 跟刚才的机构一样。那我发现我最开始的时候这边是 messes, 那 现在呢?把这个 s 去了,为什么可以去了?关键就在这里面,我给它起了个别名儿啊, 所以这个边名它是必须有的,如果这个边没有,那么你这边就必须加上一个 s。 好, 再演示一个引用第三方库,比方说还是以字节跳动的节省削流化库为例。好,我们还是,那么当你想引用一个第三方库的时候,你怎么知道这个 import 该怎么写呢? 我们需要从 github 上去搜索你需要引用的第三方库,比方说我知道它叫 sonic, 那 我在 github 上直接搜索 sonic 出来,我找到这个字节跳动的这个 sonic 项目,我点进去,点到他们的项目主页儿, 然后在地址栏 url 这边,我直接把 url 前面的 http 去掉后面的这一部分。哎,后面的这一部分 url 我 直接拷贝下来,就是我需要 import 的 内容粘贴过来, 就这样。好,那它对应的 python 名称啊,也是叫 sonic 的, 比方说我调一下它里面的那个序略化方法, sonic 点把手。好,我简来一个简单的序略化啊,就是一个简单的字母串吧,一般我们都是放一个结构体,这边我们就放一个字母串得了, 好,看一下它序列表的结果。 好,这就是代码层面啊,但是此时代码还是不能运行的啊,为什么呢?我们除了把代码写好之外,我们还需要把这个第三个库呢,放到我们的 go 点 mode 里面去。怎么放呢?有两种方式啊。第一种方式 是你直接在我们的终端运行一个命令 go get, 后面就是我们刚刚拷贝过来的这个 url 地址, go get 这个地址,然后回车。 好,那你执行这个命令的结果是什么呢?结果就是在我们的 go 点 mod 里面多了这样一些 require 啊,它会把这个 sonic 呢添加到 require 里面去,同时会多出一个 go 点 sum 文件啊,这个文件里有内容,我们不用关心, go 点 sum 都是我们的 go mod 命令自动去生成 sugar 啊,从始到终你都不需要关心, 这是第一种方式。第二种方式呢?我们再还原一下啊,我们再把这个 request 给删掉,本来是没有的嘛,对吧?同时我们把 go 点呃 sum 这个文件也删掉,我们再还原回去。第二种方式是我们通过另外一个命令, 我们通过 go mode tidied 啊,通过这个命令也可以达到跟刚才一样的效果。 我们看一下,哎, go 点 mod 里面是吧,它也多了这样一个 require 啊, go 点 sum 这个文件也生成了。 ok, 那 其实 go 点 mod 它到底是在干嘛呢? go 点 mod 它的作用就是说,当它发现你在代码里面有这样一个 import, 而你的 go 点 mod 这个文件里面没有相应的 require 的 时候呢,它会去把这个 require 添加进来, 这是自动添加,同时它会自动删除。就假如他发现你的勾点默认的文件里面有这样一些 request, 但是呢,你所有的勾文件里面没有, 比方说我把这个给注掉,你的所有的 go 文件里面并没有去使用它,这个时候呢,它会自动地从 go 点 mode 文件里面把相应的 require 给删掉。啊,这就是 go mode tidy 啊, go mode tidy 这个命令它的作用。 好,现在我们所有的工作都准备就绪了,我们把代码运行起来试一试 啊。 go, run, run 后面是什么呢?我们不需要指定到地点 go 这个文件,我们只需要指定到我们的命函数所在的内基目录就可以了。 ok, 它最终输出的是这个 json 虚拟化的结果就是 hello 这样一个带引号的这样一个字母串。 好,那这个地方我再来演示一个,比方说,我们刚刚说 max 包下面的这个 i 的 函数是可以调的,但是 sub 函数是不可以调的,因为 sub 它是以小写开头嘛, 六六减三。好, sub 函数它是以小写开头,它挎包是不可见的不能调用,那我们试一试它是不是真的无能调用。 run 一下,你看它这里报错啊,它报错是 d 点 go 的 第十四行出错了,也就是什么刚刚 sub 这行第十四行出错,它说 sub 函数呢,它不是导出的啊,就是这个 这个包下面没有导出这个 sub 函数,所以呢,你在外面是不能够直接调用的。 ok, 然后我们再来演示一个 init 函数啊, init 函数,比方说我在 b 这边,我加一个函数放 init, 那 inn 的 函数是构员里面所特有的一个函数,这个函数有点特殊性,它的名称必须叫 innit, 而且呢,它的入仓跟出仓都是空,里面的话你可以随便写。 比方说我这边是 innit, 它是出自于 myis 这个包下面的。 好,它是 message 包下面我加了一个 init 函数,那一个包下面实际上你可以加多个 init 函数,甚至是一个 购文件里面都可以存在多个 init 函数啊,这个跟普通函数很不一样,普通函数的话,你在一个 package 上面,相同的函数名只能出现一次,但是 init 的 话它却可以出现多次。 好,我们在 a 点 go 里面也给它来一个 int 函数, 这个是 int, int 这个 int 包 int 包。好,那这个函数呢?我们并没有在任何地方进行显示的调用,我们还是把刚才的代码运行一下 转起来。啊,刚才那个错还没改过来啊,我把这一行 sub 这一行删掉。 好,我们把代码再运行一下。 啊, format, 哦,忘了,那我们这边使用了 fmt 的 话,那我们在这边需要 import fmt。 好,当你只引用了一个包的时候呢,可以直接这样写,当你引用多个包的时候呢,需要把它放在一个小括号里。同样在 b 点勾里面,我们也需要 import fmt。 fmt 呢,是标准库里面的一个包,我们再把代码运行一下。 好,这个时候就可以正常运行了。那我们观察一下它跟我们刚才的结果,实际上多出来这三行啊,多出来三行, 为什么会多出来这三行呢?我们分析一下,其实就是因为在我们的 d 点 go 啊这样一个入口代码。这里面我们不是首先第五行不是先引入了 youtube 这个包吗?那么 youtube 这个包下面 就是 a 点勾里面有这样一个输出,它会去自动地去执行,你看它先去执行了 youtube package, 执行这个 int 函数,然后我们在 d 点勾里面 又引入了 math 这个包,那 math 这个包下面刚才是有两个 int 函数,对吧?它也会去 执行这两个一定的函数。所以说我们看到它的这个执行顺序,就是你在你的命令程序里面, input 里面是按照什么顺序写的,它就会依次地按照相同的顺序去执行对应包下面的一定的函数,一定的函数有多个,那么呢,它也会依次地全部执行 好,而且它们不需要显示调用啊,只需要我 import 这个包就会自动地去执行相应的 in 函数。这个操作是在命函数执行之前就已经完成的, 是吧?他们在命含有处执行之前,就先把所有的 unit 全部给执行了。 ok, 那 有时候呢,我们会看到一些开源代码里面会出现这样一种情况,比方说, 哎,这个地方它的前面加了一个下划线啊,加了个下划线空格, 然后呢,我在代码里面,我把使用 youtube 的 这两个代码删掉,我先注视掉吧,注视掉 好,我发现啊,由于我在下面代码里面完全没有使用,完全没有使用 youtube 这个包,也是我完全没有使用 a 点 go 这个文件里面的所有内容, 对吧?既然你在 mail 这里面没有使用它,那么呢,你在 import 里面呢,就不能去写它,但是呢,我还是写了。 这个时候,如果说啊,如果说你没有这个下划线的话,直接运行代码是会报错的。报什么错?就是说你引用了,但是呢,下面没有使用,我们看看 运行一下,你看他说 d 点 go 的 第五行,他说你引用了这个 youtube 这个包,但是呢,并没有使用,可以报错,是吧?哎,但是如果我们只需要在前面加一个下划线啊,加一个下划线保存一下,我们再来运行, 再来运行。好,你看这个时候就完全不报错了,对吧? 那既然说我没有引用,我没有使用 uq 这个包,那我直接把这行代码 import 这行代码直接注删掉不就可以了吗?为什么还要把它放这呢?哎,这是有作用的啊,有作用的。 我发现如果你把它放这的话,前面加个下划线有什么作用呢?我们来观察一下刚才的这个输出,你看 这一行输出这个 in 的 函数,它还是执行了的,对吧?只要你 import 里面有这个包,那么这个包下面的 in 的 函数它就会执行啊,虽然说我在代码里面并没有显示地去调用这个包里面的任何函数或者任何变量。 所以啊,以后当我们在一些看人代码里面看到别人这样写的时候, 你看到这个下划线,你心里面就应该清楚,其实它的目的就是想去自动执行这个包下面的一定函数。