Go基础—7.包(package)

TOC

一、Go包(package)

包的基本应用

包的声明:

package 包名

包的基本概念

包的用法规范:

  • 包名为 main 的包为应用程序的入口包,编译不包含 main 包的源码文件时不会得到可执行文件。
  • 一个文件夹下的所有源码文件只能属于同一个包,同样属于同一个包的源码文件不能放在多个文件夹下。
  • 包名一般小写,包名称使用简短易理解的名字。
  • 包名尽量要和所在目录同名,也可以不同,包名中不能包含-等特殊字符。
  • 包一般使用域名作为目录名称,这样能保证包名的唯一性。比如GitHub中的目录一般是GOPATH/src/github.com/userName/projectName目录下。

包的导入

包导入规范注意:

  • import导入语句通常放在源码文件的开头包声明语句下面。
  • 导入的包名需要使用双引号包裹起来;
  • 包名是从GOPATH/src/ 后开始计算的,使用/ 进行路径分隔。
    包导入的几种写法和应用,代码如下:
// 单行导入
import "包名"
// 多行导入
import (
  "包1"
  "包2"
)
// 绝对路径导入,包的绝对路径就是 GOROOT/src/ 或 GOPATH/src/ 后面包的存放路径
import "app/test"
import "app/a/b"
// 相对路径导入,相对路径只能用于导入 GOPATH 下的包,标准包的导入只能使用全路径导入
import "../a"

包的引用格式

1.标准引用格式,代码如下:

/* import "包名"
此时可以用`fmt.`作为前缀来使用 fmt 包中的方法,这是常用的一种方式。
*/
package main

import "fmt"

func main() {
    fmt.Println("我是标准引用格式。")
}

2.自定义别名引用格式,代码如下:

/* import 自定义名 "包名"
自定义名就是包名的别名,使用时我们可以使用自定义名.来代替标准引用格式的fmt.来作为前缀使用 fmt 包中的方法。
*/
package main

import F "fmt"

func main() {
    F.Println("我是自定义别名引用格式。")
}

3.省略引用格式,代码如下:

/* import . "包名"
这种格式相当于把fmt包直接合并到当前程序中,在使用fmt包内的方法是可以不用加前缀fmt.,直接引用。
*/
package main

import . "fmt"

func main() {
    Println("我是省略引用格式。")
}

4.匿名引用格式,代码如下:

/* import _ "包名"
在引用某个包时,如果只是希望执行包初始化的init函数,而不使用包内部的数据时,可以使用匿名引用格式。
*/
package main

import _ "fmt"

func main() {
    Println("匿名引用格式。")
}

包的封装和详细规范

包的封装要求

  • 1.包名与目录结构
    • Go语言的每个目录就是一个包
    • 同一个目录下的所有 .go 文件必须声明相同的包名
    • 包名可以和目录名不同,但通常保持一致以提高可读性。
  • 2.函数和变量以及结构体的导出(封装限制)
    • 使用首字母大小写来控制访问权限。大写开头:对外可见,小写开头包内私有,不对外。
  • 3.包的导入
    • 在另一个包中使用时需要 import 包路径(注意不是目录路径,而是模块路径+相对路径)
  • 4.包的初始化
    • 每个包可以有一个或多个 init() 函数,用于初始化逻辑。
    • 所有 init() 函数会在 main() 之前自动调用。
  • 5.包名不要与标准库冲突
    • 比如不要用json、fmt、http 等作为你自己写的包名,容易引起冲突或误解。
  • 6.包的依赖管理
    • 使用 Go Modules (go mod) 来管理包的依赖关系。
    • 使用 go mod init 创建模块,go mod tidy 管理依赖。
  • 7.避免包的循环引用
    • Go 不允许包之间形成循环依赖。比如:包 A import 包 B,包 B 又 import 包 A是错误的。
  • 8.结构体的封装方法
    • 结构体字段和方法的可见性也遵循首字母大小写规则。

【示例1】包的简单封装使用

下面示例封装了一个健康检测的功能模块,通过该功能模块实现传入身高体重等数据来判断我是否健康。
项目结构如下:

# 初始化包结构
go mod init myapp
# 查看项目目录结构
tree myapp
myapp
├── check_health.go
├── checker
│   └── health.go
└── go.mod

相关代码如下所示:
checker/health.go的代码,主要封装了健康检测功能的主要方法函数,通过传入结构体数据来调用该方法实现功能。代码如下:

package checker

import "fmt"

// 个人信息 结构体
type Information struct {
	Name   string
	Gender string // 可选项:Male / Female
	Age    int
	Height float64 // 单位:cm
	Weight float64 // 单位:kg
}

// 计算 BMI 值
func (p Information) CalculateBMI() float64 {
	heightM := p.Height / 100.0
	return p.Weight / (heightM * heightM)
}

// 返回健康状况描述
func (p Information) HealthStatus() string {
    // 调用CalculateBMI()函数计算出 BMI 值
	bmi := p.CalculateBMI()
	var status string
	switch {
	case bmi < 18.5:
		status = "偏瘦"
	case bmi >= 18.5 && bmi < 24.9:
		status = "正常"
	case bmi >= 25 && bmi < 27:
		status = "偏胖"
	default:
		status = "肥胖"
	}
	return fmt.Sprintf("%s(性别:%s, 年龄:%d),BMI = %.2f,健康状态:%s",
		p.Name, p.Gender, p.Age, bmi, status)
}

check_health.go的代码,应用程序入口包,主要赋值相关信息的数据,然后通过封装的checker包调用相关方法函数来计算健康值。代码如下:

package main

import (
	"fmt"
	"myapp/checker"
)

func main() {
	// 设置个人相关信息
	var (
		name   string  = "Jack"
		gender string  = "男"
		age    int     = 27
		height float64 = 170
		weight float64 = 60.3
	)

	// 初始化Information
	user := checker.Information{
		Name:   name,
		Gender: gender,
		Age:    age,
		Height: height,
		Weight: weight,
	}
	// 调用checker模块的HealthStatus()方法得出健康信息
	fmt.Println(user.HealthStatus())
}
/* 运行结果如下
Jack(性别:男, 年龄:27),BMI = 20.87,健康状态:正常
*/

【示例2】多种类方法封装使用

下面示例封装了模拟支付宝和微信支付的功能模块,将支付和退款两个功能分成两个go文件方便区分功能,但是同属于名为pay的包,再将所使用的结构体和获取方法封装到单独的go文件中,通过调用模块来实现模拟支付宝和微信支付和退款的功能。
项目结构如下:

# 初始化包结构
go mod init myapp
# 查看项目目录结构
tree myapp
myapp
├── go.mod
├── pay
│   ├── pay_struct.go
│   ├── payment.go
│   └── refund.go
└── pay.go

pay/payment.go的代码,主要封装了支付相关的函数方法,通过传入用户、金额等参数数据来调用相关的支付宝或者是微信的支付方法来模拟支付相关流程功能。代码如下:

package pay

import "fmt"

// 支付类的接口定义
type Payment interface {
	Payment_begin(user string) string
	Payment_amount(amount float64) string
	Pay_success(user string, amount float64)
}

// 实现:支付宝的相关支付方法
func (a Alipay) Payment_begin(user string) string {
	return fmt.Sprintf("正在使用支付宝为用户 %s 充值", user)
}

func (a Alipay) Payment_amount(amount float64) string {
	return fmt.Sprintf("充值金额(单位:元): %.2f", amount)
}

func (a Alipay) Pay_success(user string, amount float64) {
	fmt.Printf("支付宝为用户 %s 充值 %.2f 元成功.\n", user, amount)
}

// 实现:微信的相关支付方法
func (w WechatPay) Payment_begin(user string) string {
	return fmt.Sprintf("正在使用微信为用户 %s 充值", user)
}

func (w WechatPay) Payment_amount(amount float64) string {
	return fmt.Sprintf("充值金额(单位:元): %.2f", amount)
}

func (w WechatPay) Pay_success(user string, amount float64) {
	fmt.Printf("微信为用户 %s 充值 %.2f 元成功.\n", user, amount)
}

// 统一处理支付类操作的函数
func Process_payment(p Payment, user string, amount float64) {
	fmt.Println(p.Payment_begin(user))
	fmt.Println(p.Payment_amount(amount))
	p.Pay_success(user, amount)
}

pay/refund.go的代码,主要封装了退款相关的函数方法,通过传入用户、金额等参数数据来调用相关的支付宝或者是微信的退款方法来模拟退款相关流程功能。代码如下:

package pay

import "fmt"

// 退款类的接口定义
type Refund interface {
	Refund(amount float64) string
	Refund_success(amount float64)
}

// 实现:支付宝的相关退款方法
func (a Alipay) Refund(amount float64) string {
	return fmt.Sprintf("支付宝申请退款 %.2f 元", amount)
}

func (a Alipay) Refund_success(amount float64) {
	fmt.Printf("支付宝退款 %.2f 元成功.\n", amount)
}

// 实现:微信的相关退款方法
func (w WechatPay) Refund(amount float64) string {
	return fmt.Sprintf("微信申请退款 %.2f 元", amount)
}

func (w WechatPay) Refund_success(amount float64) {
	fmt.Printf("微信退款 %.2f 元成功.\n", amount)
}

// 统一处理退款类操作的函数
func Process_refund(r Refund, amount float64) {
	fmt.Println(r.Refund(amount))
	r.Refund_success(amount)
}

pay/pay_struct.go的代码,主要用来封装获取支付宝和微信相关的结构体。代码如下:

package pay

// 实现:支付宝的相关结构体
type Alipay struct{}

// 实现:微信支付的相关结构体
type WechatPay struct{}

func NewAlipay() *Alipay {
	return &Alipay{}
}

func NewWechatPay() *WechatPay {
	return &WechatPay{}
}

pay.go的代码,应用程序入口包,先通过调用NewAlipay()和NewWechatPay()进行初始化两个支付的结构体,再调用封装好的支付和退款的统一处理函数来实现模拟Alipay和WeChatpay的支付和退款的功能。代码如下:

package main

import "myapp/pay"

func main() {
	// 初始化Alipay和WeChatpay的结构体
	alipay := pay.NewAlipay()
	wechatpay := pay.NewWechatPay()
	// 调用封装好的方法函数模拟Alipay和WeChatpay的支付和退款
	pay.Process_payment(alipay, "2736329", 26.89)
	pay.Process_payment(wechatpay, "4785736", 100)
	pay.Process_refund(alipay, 78.9)
	pay.Process_refund(wechatpay, 100)
}
/* 运行结果如下
正在使用支付宝为用户 2736329 充值
充值金额(单位:元): 26.89
支付宝为用户 2736329 充值 26.89 元成功.
正在使用微信为用户 4785736 充值
充值金额(单位:元): 100.00
微信为用户 4785736 充值 100.00 元成功.
支付宝申请退款 78.90 元
支付宝退款 78.90 元成功.
微信申请退款 100.00 元
微信退款 100.00 元成功.
*/

GOPATH(Go语言工作目录)

GOPATH 是 Go语言中使用的一个环境变量,它使用绝对路径提供项目的工作目录。

GOPATH的基本知识

使用命令查看GOPATH信息
使用命令go env命令就可以查看当前的GOPATH路径设置情况和其他信息。

go env
------------------------------------------------
AR='ar'
CC='clang'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='clang++'
GCCGO='gccgo'
GO111MODULE=''
GOARCH='arm64'
GOARM64='v8.0'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/Users/devops/Library/Caches/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/Users/devops/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/db/kgjzsklx1yq7vcy2j28xj80h0000gn/T/go-build1377559569=/tmp/go-build -gno-record-gcc-switches -fno-common'
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMOD='/dev/null'
GOMODCACHE='/Users/devops/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/devops/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/local/go'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/Users/devops/Library/Application Support/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/local/go/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.24.1'
GOWORK=''
PKG_CONFIG='pkg-config'

重要变量详解:

  • GOARCH—表示目标处理器架构。
  • GOBIN—表示编译器和链接器的安装位置。
  • GOOS—表示目标操作系统。
  • GOPATH—表示当前工作目录。
  • GOROOT—表示 Go 开发包的安装目录。
    GOPATH的工程结构
  • $GOPATH/src:代码总是会放在这个目录下。
  • $GOPATH/bin: 在工程经过 go build、go install 或 go get 等指令后,会将产生的二进制可执行文件会放在该目录下。
  • $GOPATH/pkg:生成的中间缓存文件会放在该目录下。
    如果需要将整个源码添加到版本管理工具(Version Control System,VCS)中时,只需要添加 $GOPATH/src 目录的源码即可。bin 和 pkg 目录的内容都可以由 src 目录生成。

设置和使用GOPATH打包程序

设置GOPATH

export GOPATH="你想设置的目录路径"

基于GOPATH打包程序

1.创建GOPATH中的项目目录。使用下面的指令再 GOPATH 中的 src 目录下创建项目目录。

mkdir -p src/demo

2.添加程序相关源码。添加main.go和其他相关代码文件,并保存到$GOPATH/src/test目录下。

package main

import "fmt"

func main(){
    fmt.Println("This is a test demo.")
}

3.编译源码并运行。使用install命令Go语言中可以通过 GOPATH 找到工程的位置。

go install demo

编译完成的可执行文件会保存在 $GOPATH/bin 目录下。我们可以直接像运行命令一样运行程序。

demo
# 运行结果如下
This is a test dmeo.