Go语言自定义包
来源:云海天教程
时间:2022-12-30 20:12:41 234浏览 收藏
IT行业相对于一般传统行业,发展更新速度更快,一旦停止了学习,很快就会被行业所淘汰。所以我们需要踏踏实实的不断学习,精进自己的技术,尤其是初学者。今天golang学习网给大家整理了《Go语言自定义包》,聊聊package,我们一起来看看吧!
到目前为止,我们见过的所有例子都是以一个包的形式存在的,也就是 main 包。在 Go语言里,允许我们将同一个包的代码分隔成多个小块来单独保存,只需要将这些文件放在同一个目录即可。对于更大的应用程序,我们可能更喜欢将它的功能性分隔成逻辑的单元,分别在不同的包里实现。或者将一些应用程序通用的那一部分剖离出来。
Go语言里并没有限制一个应用程序能导入多少个包或者一个包能被多少个应用程序共享,但是将这些应用程序特定的包放在当前应用程序的子目录下和放在 GOPATH 源码目录下是不大一样的。
我们所说的 GOPATH 源码目录是一个叫 src 的目录,每一个在 GOPATH 环境变量里的的目录都应该包含这个目录,因为 Go语言的工具链就是要求这样做的。我们的程序和包都应该在这个 src 目录下的子目录里。
我们也可以将我们自己的包安装到 Go语言包目录树下,也就是 GOROOT 下,但是这样没有什么好处而且可能会不太方便,因为有些系统是通过包管理系统来安装 Go语言的,有些是通过安装包,有些是手动编译的。
创建自定义的包
我们创建的自定义的包最好就放在 GOPATH 的 src 目录下(或者 GOPATH src 的某个子目录),如果这个包只属于某个应用程序,可以直接放在应用程序的子目录下,但如果我们希望这个包可以被其他的应用程序共享,那就应该放在 GOPATH 的 src 目录下,每个包单独放在一个目录里,如果两个不同的包放在同一个目录下,会出现名字冲突的编译错误。作为惯例,包的源代码应放在一个同名的文件夹下面。同一个包可以有任意多个文件,文件的名字也没有任何规定(但后续名必须是 .go),这里我们假设包名就是 .go 的文件名(如果一个包有多个 .go 文件,则其中会有一个 .go 文件的文件名和包名相同)。
在前面讲解的 stacker 例子由一个主程序(在 stacker.go 文件里)和一个自定义的 stack 包(在文件 stack.go 里)组成,源码目录的层次结构如下:
aGoPath/src/stacker/stacker.go
aGoPath/src/stacker/stack/stack.go
我们在 stacker 目录里执行 go build 命令,就会得到一个 stacker 的可执行文件(在 Windows 系统上是 stacker.exe)。但是,如果我们希望生成的可执行文件放到 GOPATH 的 bin 目录里,或者想将 stacker/stack 包共享给其他的应用程序使用,这就必须使用 go install 来完成。
当执行 go install 命令创建 stacker 程序时,会创建两个目录(如果不存在就会创建):aGoPath/bin 和 aGoPath/pkg/linux_amd64/stacker,前者包含了 stacker 可执行文件,后者包含了 stack 包的静态库文件(至于 linux_amd64 等会根据不同的系统和硬件体系结构而变化,例如在 32 位的 Windows 系统上是 windows_386)。
需要在 stacker 程序中使用 stack 包时,在程序源文件中使用导入语句 import "stacker/stack" 即可,也就是绝对路径(Unix 风格)去除 aGoPath/src 这部分。事实上,只要这个包放在 GOPATH 下,都可以被别的程序或者包导入,GOPATH 下的包没有共享和专用之分。
又比如实现的有序映射是在 omap 包里,它被设计为可由多个程序使用。为了避免包名的冲突,我们在 GOPATH (如果 GOPATH 有多个路径,任意一个路径都可以)路径下创建了一个具有唯一名字(这里用了域名)的目录,结构如下:
aGoPath/src/qtrac.eu/omap/omap.go
这样其他的程序,只要它们也在某个 GOPATH 目录下面,都可以通过使用 import "qtrac.eu/omap" 来导入这个包。如果我们还有其他的包需要共享,则将它们放到 aGoPath/src/qtrac.eu 路径下即可。当使用 go install 安装 omap 包的时候,它创建了 aGoPath/pkg/linux_amd64/qtrac.eu 目录(如果不存在的话),保存了 omap 包的静态库文件,其中 linux_amd64 是根据不同的系统和硬件体系结构而变化的。
如果我们希望在一个包里创建新的包,例如,在 my_package 包下面创建两个新的包 pkg1 和 pkg2,可以这么做:在 aGoPath/src/my_package 建两个子目录,例如 aGoPath/src/my_package/pkg1 和 aGoPath/src/my_package/pkg2,对应的包文件是 aGoPath/src/my_package/pkg1/pkg1.go 和 aGoPath/src/my_package/pkg2/pkg2.go。
之后,假如想导入 pkg2,使用 import my_package/pkg2 即可。Go语言标准库的源码树就是这样的结构。当然,my_package 目录可以有它自己的包,如 aGoPath/src/my_package/my_package.go 文件。
Go语言中的包导入的搜索路径是首先到 GOROOT(即 $GOROOT/pkg/${GOOS}_${GOARCH},比如 /opt/go/pkg/linux_amd64),然后是 GOPATH 环境变量下的目录。这就意味这可能会有名字冲突。最简单的方法就是确保 GOPATH 里包含的每个路径都是唯一的,例如之前我们以域名来作为 omap 的包的目录。
在 Go 程序里使用标准库里的包和使用 GOPATH 路径下的包是一样的,下面几个小节我们来讨论一些平台特定的代码。
平台特定的代码
在某些情况下,我们必须为不同的平台编写一些特定的代码。例如,在类 Unix 的系统上,通常 shell 都支持通配符(也叫 globbing),所以在命令行输入 *.txt,程序就能够从 os.Args[1:] 切片里读取到比如 ["README.txt", "INSTALL.txt"] 这些值。但是在 Windows 平台上,程序只会接收到我们可以使用 filepath.Glob() 函数来实现通配符的功能,但是这只需要在 Windows 平台上使用。
那如何决定什么时候才需要使用 filepath.Glob() 函数呢,使用
if runtime.GOOS == "windows” {...}
即可,例如 cgrep1/cgrep1.go 程序等等。另一种办法就是使用平台特定的代码来实现,例如,cgrep3 程序有 3 个文件,cgrep.go、util_linux.go、util_windows.go,其中 util_linux.go 定义了这么一个函数:func commandLineFiles(files []string) []string { return files }
很明显,这个函数并没有处理文件名通配,因为在类 Unix 系统上没必要这么做。而 util_windows.go 文件则定义了另一个同名的函数。func commandLineFiles(files []string) []string { args := make([]string, 0, len(files)) for _, name := range files { if matches, err := filepath.Glob(name); err != nil { args = append (args, name) // 无效模式 } else if matches ! = nil { // 至少有一个匹配 args = append(args, matches...) } } return args}当我们使用 go build 来创建 cgrep3 程序时,在 Linux 机器上 util_linux.go 文件会被编译而 util_windows.go 则被忽略,而在 Windows 平台恰好相反,这样就确保了只有一个 commandLineFiles() 函数被实际编译了。
在 Mac OS X 系统和 FreeBSD 系统上,既不会编译 util_linux.go 也不会编译 util_windows.go,所以 go build 会返回失败。但是我们可以创建一个软链接或者直接复制 util_linux.go 到 util_darwin.go 或者 util_freebsd.go,因为这两个平台的 shell 也是支持通配符的,这样就能正常构建 Mac OS X 和 FreeBSD 平台的程序了。
文档化相关的包
如果我们想共享一些包,为了方便其他的开发者使用,需要编写足够的文档才行。Go语言提供了非常方便的文档化工具 godoc,可以在命令行显示包的文档和函数,也可以作为一个 Web 服务器启动,如下图所示。图:omap 包的文档
godoc 会自动搜索 GOPATH 路径下的所有包并将它显示出来,如果某些包不在 GOPATH 路径下,可以使用 -path 参数(除此之外还要有 -http 参数)来指定。
好的文档应该怎么写,这是一个一直争论不休的问题,因此在这一节我们只是纯粹地来了解一下 Go语言的文档化机制。
在默认情况下,只有可导出的类型、类、常量和变量才会在 godoc 里出现,因此全部这些内容应该添加合适的文档。文档都是直接包含在源文件里。这里以 omap 包为例。
// Package omap implements an efficient key-ordered map.
//
// Keys and values may be of any type, but all keys must be comparable
// using the less than function that is passed in to the omap.New()
// function, or the less than function provided by the omap.New*()
// construction functions.
package omap
// Map is a key-ordered map.
// The zero value is an invalid map! Use one of the construction functions
// (e.g., New()), to create a map for a specific key type.
type Map struct {
// New returns an empty Map that uses the given less than function to
// compare keys. For example:
// type Point { X, Y int }
// pointMap := omap.New(func(a, b interface{}) bool {
// a, p := a.(Point), b.(Point)
// if a.X != p.X {
// return a.X // }
// return a.Y // })
func New(less func(interface{}, interface{}) bool) *Map {
下图以 Web 的方式展示了一个函数的文档是什么样的,同时注释里缩进的文本会被视为代码显示在 HTML 页面上。但是 godoc 还不支持任何标记,例如 bold、italic、links 等。
// NewCaseFoldedKeyed returns an empty Map that accepts case-insensitive
// string keys.
func NewCaseFoldedKeyed() *Map {
图:omap 包中 New ()函数的文档
上面这段文档是描述了一个出于便捷方面考虑而提供的辅助构造函数,基于一个预定义的比较函数。
// Insert inserts a new key-value into the Map and returns true; or
// replaces an existing key-value pair`s value if the keys are equal and
// returns false. For example:
// inserted := myMap.Insert(key, value).
func (m *Map) Insert(key, value interface{}) (inserted bool) {
导入包
Go语言允许我们对导入的包使用别名来标识。这个特性是非常方便和有用的,例如,可以在不同的实现之间进行自由的切换。举个例子,假如我们实现了 bio 包的两个版本 bio_v1 和 bio_v2,现在在某个程序里使用了 import bio "bio_v1",如果需要切换到另一个版本的实现,只需要将 bio_v1 改成 bio_v2 即可,即 import bio "bio_v2",但是需要注意的是,bio_v1 和 bio_v2 的 API 必须是相同的,或者 bio_v2 是 bio_v1 的超集,这样其余所有的的代码都不需要做任何改动。另外,最好就不要对官方标准库的包使用别名,因为这样可能会导致一些混淆或激怒后来的维护者。当导入一个包时,它所有的 init() 函数就会被执行。有些时候我们并非真的需要使用这些包,仅仅是希望它的 init() 函数被执行而已。
举个例子,如果我们需要处理图像,通常会导入 Go 标准库支持的所有相关的包,但是并不会用到这些包的任何函数。下面就是 imagetag1.go 程序的导入语句部分。
import (
"fmt"
"image"
"os"
"path/filepath"
"runtime”
_ "image/gif"
_ "image/jpeg"
_ "image/png"
)
好了,本文到此结束,带大家了解了《Go语言自定义包》,希望本文对你有所帮助!关注golang学习网公众号,给大家分享更多Golang知识!
-
113 收藏
-
500 收藏
-
172 收藏
-
464 收藏
-
105 收藏
-
165 收藏
-
473 收藏
-
377 收藏
-
384 收藏
-
246 收藏
-
110 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习
-
- 儒雅的秀发
- 感谢大佬分享,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢作者大大分享技术贴!
- 2023-03-27 07:18:50
-
- 喜悦的果汁
- 这篇文章真及时,细节满满,赞 👍👍,已加入收藏夹了,关注作者大大了!希望作者大大能多写Golang相关的文章。
- 2023-01-22 10:34:32
-
- 不安的溪流
- 这篇技术贴真及时,太详细了,受益颇多,mark,关注博主了!希望博主能多写Golang相关的文章。
- 2023-01-11 21:09:30
-
- 舒适的玫瑰
- 感谢大佬分享,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢师傅分享博文!
- 2023-01-11 00:36:39
-
- 美好的画笔
- 这篇文章真及时,细节满满,很好,已加入收藏夹了,关注楼主了!希望楼主能多写Golang相关的文章。
- 2023-01-03 03:46:37