Skip to content

包管理

包(package)是 Go 语言代码组织和复用的基本单位。理解包管理对于编写可维护的 Go 程序至关重要。

📋 学习目标

  • 理解包的概念和作用
  • 掌握包的声明和导入
  • 理解可见性规则
  • 学会使用 Go Modules
  • 掌握包的初始化
  • 了解包的最佳实践

🎯 包基础

什么是包

包是相关功能的集合,每个 Go 源文件都属于一个包。

go
package main

import "fmt"

func main() {
	fmt.Println("Hello, Go!")
}

包的声明

每个 Go 文件必须以 package 声明开始:

go
package packageName

main 包

main 包是特殊的包,包含程序入口点 main() 函数。

📦 包的导入

基本导入

go
import "fmt"
import "os"
import "strings"

分组导入

go
import (
	"fmt"
	"os"
	"strings"
)

导入别名

go
import (
	"fmt"
	f "fmt"  // 别名
	. "fmt"  // 点导入(不推荐)
	_ "fmt"  // 空白导入(仅执行 init)
)

示例

go
package main

import (
	"fmt"
	"math"
	"strings"
)

func main() {
	// 使用标准库包
	fmt.Println("Hello")
	fmt.Println(math.Sqrt(16))
	fmt.Println(strings.ToUpper("hello"))
}

🔒 可见性规则

Go 使用首字母大小写控制可见性:

  • 大写字母开头: 公开(可被其他包访问)
  • 小写字母开头: 私有(仅包内可见)

示例

go
// package user
package user

// 公开类型
type User struct {
	Name string  // 公开字段
	age  int     // 私有字段
}

// 公开函数
func NewUser(name string) *User {
	return &User{Name: name}
}

// 私有函数
func validateName(name string) bool {
	return len(name) > 0
}
go
// package main
package main

import "user"

func main() {
	u := user.NewUser("张三")
	fmt.Println(u.Name)  // ✅ 可以访问
	// fmt.Println(u.age) // ❌ 编译错误
}

📚 Go Modules

Go Modules 是 Go 1.11+ 引入的依赖管理系统。

初始化模块

bash
go mod init github.com/username/project

这会创建 go.mod 文件:

go
module github.com/username/project

go 1.21

require (
	github.com/gin-gonic/gin v1.9.1
)

添加依赖

bash
# 添加依赖
go get github.com/gin-gonic/gin

# 添加特定版本
go get github.com/gin-gonic/gin@v1.9.1

# 更新依赖
go get -u github.com/gin-gonic/gin

整理依赖

bash
go mod tidy

查看依赖

bash
go list -m all
go mod graph

🏗️ 包结构

标准项目结构

project/
├── go.mod
├── go.sum
├── main.go
├── cmd/
│   └── app/
│       └── main.go
├── internal/
│   ├── config/
│   └── handler/
├── pkg/
│   └── utils/
└── api/
    └── routes.go

目录说明

  • cmd/: 应用程序入口
  • internal/: 内部包(外部不可导入)
  • pkg/: 可被外部导入的包
  • api/: API 相关代码

🔄 包的初始化

init 函数

每个包可以有一个或多个 init() 函数,在包被导入时自动执行。

go
package config

import "fmt"

var AppName string

func init() {
	fmt.Println("config 包初始化")
	AppName = "MyApp"
}

func init() {
	fmt.Println("第二个 init 函数")
}

初始化顺序

  1. 导入的包先初始化
  2. 包级变量初始化
  3. init() 函数执行
  4. main() 函数执行
go
package main

import (
	"fmt"
	_ "config"  // 仅执行 init
)

func init() {
	fmt.Println("main 包的 init")
}

func main() {
	fmt.Println("main 函数")
}

📦 创建自己的包

步骤 1: 创建包目录

bash
mkdir -p mypackage
cd mypackage

步骤 2: 创建包文件

go
// mypackage/math.go
package mypackage

// Add 计算两个数的和
func Add(a, b int) int {
	return a + b
}

// Subtract 计算两个数的差
func Subtract(a, b int) int {
	return a - b
}

步骤 3: 使用包

go
// main.go
package main

import (
	"fmt"
	"./mypackage"  // 本地包
)

func main() {
	sum := mypackage.Add(10, 5)
	fmt.Println(sum)
}

🎯 标准库包

常用标准库

  • fmt: 格式化 I/O
  • os: 操作系统接口
  • strings: 字符串操作
  • strconv: 字符串转换
  • time: 时间操作
  • net/http: HTTP 客户端和服务器
  • encoding/json: JSON 编码解码
  • database/sql: 数据库接口

示例

go
package main

import (
	"encoding/json"
	"fmt"
	"time"
)

type User struct {
	Name      string    `json:"name"`
	CreatedAt time.Time `json:"created_at"`
}

func main() {
	user := User{
		Name:      "张三",
		CreatedAt: time.Now(),
	}
	
	data, _ := json.Marshal(user)
	fmt.Println(string(data))
}

🔍 包查找

导入路径规则

  1. 标准库: "fmt", "os"
  2. 第三方包: "github.com/user/repo"
  3. 本地包: "./local" 或相对路径

GOPATH vs Go Modules

  • GOPATH: 旧的方式,需要设置 GOPATH 环境变量
  • Go Modules: 新方式,使用 go.mod 管理依赖

🏃‍♂️ 实践练习

练习 1: 创建工具包

创建一个 utils 包,包含常用的工具函数。

练习 2: 创建配置包

创建一个 config 包,用于管理应用配置。

练习 3: 模块化项目

将一个大型项目拆分成多个包。

🤔 思考题

  1. 包和模块有什么区别?
  2. 如何设计一个好的包结构?
  3. init 函数的使用场景有哪些?
  4. 什么时候应该使用 internal 包?
  5. 如何管理大型项目的依赖?

📚 扩展阅读

⏭️ 下一章节

并发编程 → 学习 Go 语言的并发编程特性


💡 提示: 良好的包设计是编写可维护代码的基础,遵循 Go 的包管理规范可以让代码更加清晰和易于理解!

基于 VitePress 构建