golang中的包

现今即便是个人开发的一般程序,其包含的函数,这些函数代码一般都有他人编写并打包为包或者模块的形式,并通过相关社区并发,最后有软件通过调用这些包或者模块的函数来更高效的完成开发。通过包或者模块的形式重用代码使得编程开发变得轻松

go语言包,fmt  os等这样具有常用功能的内置包在go语言中一百多个,我们称为标准库,这些标准包大部分都内置到go语言本身

   大部分编程语言都存在包的概念,而任何一种包设计的目的都是为了简化大型软件设计和维修工作 ,实际上包是函数和数据的集合。通过吧一组有关特性的函数和数据放进一个单元中,方便使用和更新相应的模块。这种包系统的设计使得每一个模块包都与程序,其他单元保持一定的独立性。

在编写代码的过程中,不同模块中间为了实现某一个类似的功能可能会采用相同的名字去命名一个函数,如果一个软件开发过程中需要同事使用两个包,就会在调函数时产生歧义。为了解决这个问题,go语言引入了命名空间的概念,让每个包都定义一个命名空间,用于内部标识符的访问,因为每个命名空间关联一个特定的包,这使得我们在调用类型,函数时有了独一无二的简短明了的名字。

为了提高包的独立性以及安全性,go语言的包可以通过控制包内名字的可见性来实现包的封装,通过限制包成员的可见性,隐藏具体的实现过程可以极大提高软件的安全性,同时开发人员调用时也不必关心其实现过程,直接使用包的api,另一个方面也允许包的维护者在不影响包的用户使用的前提下调整包的内部实现,通过限定包内函数与变量的可见性约束用户操作,只允许用户通过特定的函数来访问和控制包内部的变量,这样不仅可以提高安全性还可以保持并发操作是的互斥约束

 与大部分编译类似,go语言中,当改动了一个源文件时,就必须重新编译该源文件,以及他对应的包和所有依赖该包的包。即使是从头构建,go语言编译器的编译速度也明显快于绝大部分编译语言,得益于其包设计的三个特性。

显示声明:所有导入的包必须在每个文件的开头显示声明,这样编译器就没必要读取和分析整个源文件来判断包的依赖关系

无环遍历:禁止包的环状依赖,因为没有循环依赖,包的依赖关系形成一个有向无环图,每个包可以被独立编译,而且很可能是被并发编译

无须遍历:编译后的目标文件不仅记录包本身的导出信息,同时还记录了包的依赖关系,因此在编译一个包的时候,编译器只需要读取每个直接导入包的目标文件,而不需要遍历所有依赖文件,毕竟很多都是重复的间接依赖

 包的结构

go语言编译工具对源码目录有很严格的要求,每个工作空间必须由bin  pkg  src三个目录组成。bin目录主要存放可执行文件,pkg目录存放编译好的库文件,主要是*.a文件;src目录主要存放go语言的源文件。

因为go语言采用了工作空间这种方式管理本地代码,这与大部分编程语言不一样,因为这里解释一下goroot与gopath之间的关系,首先显而易见的一点就是goroot是一个全局并且唯一的变量,用于指定存放go语言本身的目录路径。gopath是一个工作空间的变量,他可以有多个,用于指定工作空间的目录路径

 通常go get会使用第一个工作空间保存下载的第三方库,在开发时不管是哪个工作空间下载的包都可以在任意空间使用,需要注意的一点就是尽量不要吧goroot gopath设置为同一个

包的源文件,包的代码必须全部都放在包中,并且源文件头部都必须一直使用package name的语句进行申明,go语言包可以由多个文件组成,所以文件名不需要与包名一直,包名建议使用小写字符,包名类型命名空间,与包所在目录编译文件名无关。目录名尽量不要保留名称,对于可执行文件必须包含package main以及入口函数main

go语言使用名称首字母大小写来判断一个对象,全局变量,全局常量,类型,结构字段,函数,方法的访问权限。对于包而言同样如此,包中成员名称首字母大小写决定了该成员的访问权限,首字母大写,可被包外访问;首字母小写,仅包内访问

与大部分现代编程语言一样,go语言同样支持使用utf-8字符来命名对象。如果使用汉字作为一个函数的名称,该函数默认是私有的,需要在汉字前面加上一个大写字母才能使其变为共有函数。

包的声明,上面提到的每一个包内源文件都需要开头声明所在包,这其实就是包的声明。包的声明对于包内而言主要用于源文件编译时能够为编译器指明哪个是包的源代码,对于包外而言,在导入包的时候可以使用包名.函数名,使用包内函数

对于包名相同的情况,关于包的声明有一个例外,那就是包编译后是一个可执行程序是,会使用package main的方式声明main包,这时候包编译完之后必须调用连接器生成一个可执行程序

 包的导入,如前所述,使用包成员之前前导入包,导入包的关键字是import因为go语言包不能形成环形依赖,如果遇到导入包循环依赖的情况,go语言的构建工具将返回操作。相对目录是从pkg开始的子目录

导入声明,虽然包的命名空间解决了函数重名的情况,但是没有避免包重名情况。

import "crypto/rand"
import  R "crypto/rand"
import . "crypto/rand"
import _ "crypto/rand"

go语言不允许导入包却不使用,如果导入包并不使用,在编译时会被视为错误

包的重命名不仅用于解决包名冲突,还可以解决包名过程,避免与变量或常量名冲突的情况

当前go语言的规范并没有强制包的导入路径字符串的格式,导入路径由构建工具来解释。但如果打算分享或自己发布编写的包,最好使用全球唯一的导入路径。

import (
  "fmt"
   "math/rand"
   "encoding/json" 
   "golang.org/x/net/html"
   "github.com/go-sql-driver/mysql"

)
package main

import (
	"fmt"
	"net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, `<meta name="go-import" 
	 content="example.com/zuolan/test git https://githua.com/zuolan/test>`)
}
func main() {
	http.HandleFunc("/zuolan/test", handler)
	http.ListenAndServe(":80", nil)
}

匿名导入

如果只是导入一个包而并不使用导入的包将会导致一个编译错误。但是有时候我们只是想利用导入包而产生的副作用,他会计算包级变量的初始化表达式和执行导入包的ini初始化函数。这时候需要一直unused import编译错误。

import  _  "image/png"  

这个称为包的匿名导入,他通常用来实现一个编译时机制,然后通过再main主程序入口选择性的导入附加的包。

 标准库image图像包包含一个decode函数,用于从io.reader接口读取数据并解码图形,它调用底层注册的图像解码器来完成任务,然后返回image.Image类型的图像。使用imgae.Decode很容易编写一个图像格式的转换工具,读取一种合适的图像,然后编码为另外一种图像格式

package main

import (
	"fmt"
	"image"
	"image/jpeg"
	_ "image/png"
	"io"
	"os"
)

func main() {
	if err := toJPEG(os.Stdin, os.Stdout); err != nil {
		fmt.Fprintf(os.Stderr, "jpeg:%v\n", err)
		os.Exit(1)
	}
}
func toJPEG(in io.Reader, out io.Writer) error {
	img, kind, err := image.Decode(in)
	if err != nil {
		return err
	}

	fmt.Println(os.Stderr, "Input format = ", kind)
	return jpeg.Encode(out, img, &jpeg.Options{Quality: 95})
}

如果将输出导入一个程序的标注输入,它将解码输入的PNG格式图像。image/png包的匿名导入语句,程序依然可以编译和运行,但是它将不能正确的识别和解码。标准课还提供了gif,png,jpeg等格式图像的解码器,为了保持程序体积小,很多解码器并没有全部包含,除非是明确需要支持的格式

image.Decode函数在解码时会依次查询支持的格式列表,每个格式驱动列表的每个入口指定了四件事情。格式的名称:一个用于描述这种图像数据开头部分模式的字符串,用于解码器检测识别;一个decode函数用于完成解码图像工作;一个decodeConfig函数用于解码图像的大小和颜色空间的信息。每个驱动入口是通过调用image.RegisterFormat函数注册的,一般实在每个格式包的init初始化函数中调用的。

最终的效果是,主程序只需要匿名导入特定图像驱动包就可以image.Decode解码对应格式的图像了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值