完成
This commit is contained in:
665
golang-learning/08-packages/01-creating-packages.go
Normal file
665
golang-learning/08-packages/01-creating-packages.go
Normal file
@@ -0,0 +1,665 @@
|
||||
/*
|
||||
01-creating-packages.go - Go 语言包创建详解
|
||||
|
||||
学习目标:
|
||||
1. 理解 Go 语言包的概念和作用
|
||||
2. 掌握包的创建和组织方式
|
||||
3. 学会包的命名规范和最佳实践
|
||||
4. 了解包的可见性规则
|
||||
5. 掌握包的文档编写
|
||||
|
||||
知识点:
|
||||
- 包的基本概念和作用
|
||||
- 包的创建和组织
|
||||
- 包的命名规范
|
||||
- 导出和未导出标识符
|
||||
- 包的文档注释
|
||||
- 包的初始化
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("=== Go 语言包创建详解 ===\\n")
|
||||
|
||||
// 演示包的基本概念
|
||||
demonstratePackageBasics()
|
||||
|
||||
// 演示包的组织结构
|
||||
demonstratePackageOrganization()
|
||||
|
||||
// 演示包的命名规范
|
||||
demonstratePackageNaming()
|
||||
|
||||
// 演示可见性规则
|
||||
demonstrateVisibilityRules()
|
||||
|
||||
// 演示包的文档
|
||||
demonstratePackageDocumentation()
|
||||
|
||||
// 演示包的初始化
|
||||
demonstratePackageInitialization()
|
||||
|
||||
// 演示实际包创建示例
|
||||
demonstratePracticalPackageCreation()
|
||||
}
|
||||
|
||||
// demonstratePackageBasics 演示包的基本概念
|
||||
func demonstratePackageBasics() {
|
||||
fmt.Println("1. 包的基本概念:")
|
||||
|
||||
// 包的基本概念
|
||||
fmt.Printf(" 包的基本概念:\\n")
|
||||
fmt.Printf(" - 包是 Go 语言组织代码的基本单位\\n")
|
||||
fmt.Printf(" - 每个 Go 文件都必须属于一个包\\n")
|
||||
fmt.Printf(" - 包提供了命名空间和封装机制\\n")
|
||||
fmt.Printf(" - 包可以被其他包导入和使用\\n")
|
||||
fmt.Printf(" - main 包是程序的入口点\\n")
|
||||
|
||||
// 包的作用
|
||||
fmt.Printf(" 包的作用:\\n")
|
||||
fmt.Printf(" 1. 代码组织:将相关功能组织在一起\\n")
|
||||
fmt.Printf(" 2. 命名空间:避免命名冲突\\n")
|
||||
fmt.Printf(" 3. 封装性:控制代码的可见性\\n")
|
||||
fmt.Printf(" 4. 重用性:便于代码重用和分享\\n")
|
||||
fmt.Printf(" 5. 模块化:支持大型项目的模块化开发\\n")
|
||||
|
||||
// 包的类型
|
||||
fmt.Printf(" 包的类型:\\n")
|
||||
fmt.Printf(" 1. main 包:可执行程序的入口\\n")
|
||||
fmt.Printf(" 2. 库包:提供功能给其他包使用\\n")
|
||||
fmt.Printf(" 3. 标准库包:Go 官方提供的包\\n")
|
||||
fmt.Printf(" 4. 第三方包:社区开发的包\\n")
|
||||
fmt.Printf(" 5. 内部包:项目内部使用的包\\n")
|
||||
|
||||
// 包声明
|
||||
fmt.Printf(" 包声明:\\n")
|
||||
fmt.Printf(" - 每个 Go 文件的第一行必须是包声明\\n")
|
||||
fmt.Printf(" - 格式:package 包名\\n")
|
||||
fmt.Printf(" - 同一目录下的所有 .go 文件必须属于同一个包\\n")
|
||||
fmt.Printf(" - 包名通常与目录名相同(但不是必须的)\\n")
|
||||
|
||||
// 示例包声明
|
||||
fmt.Printf(" 示例包声明:\\n")
|
||||
packageExamples := []string{
|
||||
"package main // 可执行程序",
|
||||
"package utils // 工具包",
|
||||
"package http // HTTP 相关功能",
|
||||
"package database // 数据库操作",
|
||||
"package config // 配置管理",
|
||||
}
|
||||
|
||||
for _, example := range packageExamples {
|
||||
fmt.Printf(" %s\\n", example)
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// demonstratePackageOrganization 演示包的组织结构
|
||||
func demonstratePackageOrganization() {
|
||||
fmt.Println("2. 包的组织结构:")
|
||||
|
||||
// 项目结构示例
|
||||
fmt.Printf(" 典型的项目结构:\\n")
|
||||
fmt.Printf(" myproject/\\n")
|
||||
fmt.Printf(" ├── main.go // 主程序\\n")
|
||||
fmt.Printf(" ├── go.mod // 模块定义\\n")
|
||||
fmt.Printf(" ├── README.md // 项目说明\\n")
|
||||
fmt.Printf(" ├── cmd/ // 命令行程序\\n")
|
||||
fmt.Printf(" │ └── server/\\n")
|
||||
fmt.Printf(" │ └── main.go\\n")
|
||||
fmt.Printf(" ├── pkg/ // 公共库\\n")
|
||||
fmt.Printf(" │ ├── config/\\n")
|
||||
fmt.Printf(" │ │ └── config.go\\n")
|
||||
fmt.Printf(" │ └── utils/\\n")
|
||||
fmt.Printf(" │ └── helper.go\\n")
|
||||
fmt.Printf(" ├── internal/ // 内部包\\n")
|
||||
fmt.Printf(" │ ├── auth/\\n")
|
||||
fmt.Printf(" │ │ └── auth.go\\n")
|
||||
fmt.Printf(" │ └── database/\\n")
|
||||
fmt.Printf(" │ └── db.go\\n")
|
||||
fmt.Printf(" ├── api/ // API 相关\\n")
|
||||
fmt.Printf(" │ ├── handlers/\\n")
|
||||
fmt.Printf(" │ │ └── user.go\\n")
|
||||
fmt.Printf(" │ └── middleware/\\n")
|
||||
fmt.Printf(" │ └── auth.go\\n")
|
||||
fmt.Printf(" ├── models/ // 数据模型\\n")
|
||||
fmt.Printf(" │ ├── user.go\\n")
|
||||
fmt.Printf(" │ └── product.go\\n")
|
||||
fmt.Printf(" ├── services/ // 业务逻辑\\n")
|
||||
fmt.Printf(" │ ├── user.go\\n")
|
||||
fmt.Printf(" │ └── product.go\\n")
|
||||
fmt.Printf(" ├── tests/ // 测试文件\\n")
|
||||
fmt.Printf(" │ └── integration/\\n")
|
||||
fmt.Printf(" └── docs/ // 文档\\n")
|
||||
fmt.Printf(" └── api.md\\n")
|
||||
|
||||
// 包组织原则
|
||||
fmt.Printf(" 包组织原则:\\n")
|
||||
fmt.Printf(" 1. 按功能分组:相关功能放在同一个包中\\n")
|
||||
fmt.Printf(" 2. 单一职责:每个包应该有明确的职责\\n")
|
||||
fmt.Printf(" 3. 依赖管理:避免循环依赖\\n")
|
||||
fmt.Printf(" 4. 接口分离:定义清晰的包接口\\n")
|
||||
fmt.Printf(" 5. 层次结构:建立合理的包层次\\n")
|
||||
|
||||
// 目录命名规范
|
||||
fmt.Printf(" 目录命名规范:\\n")
|
||||
fmt.Printf(" - 使用小写字母\\n")
|
||||
fmt.Printf(" - 使用短而有意义的名称\\n")
|
||||
fmt.Printf(" - 避免使用下划线和连字符\\n")
|
||||
fmt.Printf(" - 使用复数形式(如 handlers, models)\\n")
|
||||
fmt.Printf(" - 避免使用 Go 关键字\\n")
|
||||
|
||||
// 特殊目录
|
||||
fmt.Printf(" 特殊目录:\\n")
|
||||
fmt.Printf(" - cmd/: 存放可执行程序的源码\\n")
|
||||
fmt.Printf(" - pkg/: 存放可以被外部应用使用的库代码\\n")
|
||||
fmt.Printf(" - internal/: 存放私有应用和库代码\\n")
|
||||
fmt.Printf(" - vendor/: 存放依赖包的副本\\n")
|
||||
fmt.Printf(" - testdata/: 存放测试数据\\n")
|
||||
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// demonstratePackageNaming 演示包的命名规范
|
||||
func demonstratePackageNaming() {
|
||||
fmt.Println("3. 包的命名规范:")
|
||||
|
||||
// 命名规则
|
||||
fmt.Printf(" 包命名规则:\\n")
|
||||
fmt.Printf(" 1. 使用小写字母\\n")
|
||||
fmt.Printf(" 2. 简短而有意义\\n")
|
||||
fmt.Printf(" 3. 避免使用下划线\\n")
|
||||
fmt.Printf(" 4. 不要使用复数形式\\n")
|
||||
fmt.Printf(" 5. 避免与标准库冲突\\n")
|
||||
|
||||
// 好的包名示例
|
||||
fmt.Printf(" 好的包名示例:\\n")
|
||||
goodNames := []string{
|
||||
"http", // 简短明了
|
||||
"json", // 广为人知的缩写
|
||||
"url", // 常用缩写
|
||||
"time", // 简单直接
|
||||
"crypto", // 清晰的功能描述
|
||||
"database", // 完整但不冗长
|
||||
"config", // 简洁的功能描述
|
||||
"auth", // 常用缩写
|
||||
}
|
||||
|
||||
for _, name := range goodNames {
|
||||
fmt.Printf(" %s\\n", name)
|
||||
}
|
||||
|
||||
// 不好的包名示例
|
||||
fmt.Printf(" 不好的包名示例:\\n")
|
||||
badNames := map[string]string{
|
||||
"HTTP": "使用了大写字母",
|
||||
"http_client": "使用了下划线",
|
||||
"utils": "太通用,没有明确含义",
|
||||
"helpers": "使用了复数形式",
|
||||
"mypackage": "没有描述功能",
|
||||
"data_base": "使用了下划线",
|
||||
"user_service": "使用了下划线",
|
||||
"common": "太通用",
|
||||
}
|
||||
|
||||
for name, reason := range badNames {
|
||||
fmt.Printf(" %s - %s\\n", name, reason)
|
||||
}
|
||||
|
||||
// 包名与导入路径
|
||||
fmt.Printf(" 包名与导入路径:\\n")
|
||||
fmt.Printf(" - 包名是 package 声明中的名称\\n")
|
||||
fmt.Printf(" - 导入路径是 import 语句中的路径\\n")
|
||||
fmt.Printf(" - 包名通常是导入路径的最后一部分\\n")
|
||||
fmt.Printf(" - 但包名和目录名可以不同\\n")
|
||||
|
||||
// 示例
|
||||
fmt.Printf(" 示例:\\n")
|
||||
fmt.Printf(" 导入路径: github.com/user/project/pkg/database\\n")
|
||||
fmt.Printf(" 包名: package database\\n")
|
||||
fmt.Printf(" 使用: database.Connect()\\n")
|
||||
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// demonstrateVisibilityRules 演示可见性规则
|
||||
func demonstrateVisibilityRules() {
|
||||
fmt.Println("4. 可见性规则:")
|
||||
|
||||
// 可见性基本规则
|
||||
fmt.Printf(" 可见性基本规则:\\n")
|
||||
fmt.Printf(" - 首字母大写:导出(公开),可被其他包访问\\n")
|
||||
fmt.Printf(" - 首字母小写:未导出(私有),只能包内访问\\n")
|
||||
fmt.Printf(" - 适用于:函数、变量、常量、类型、方法、字段\\n")
|
||||
|
||||
// 导出标识符示例
|
||||
fmt.Printf(" 导出标识符示例:\\n")
|
||||
exportedExamples := []string{
|
||||
"func Add(a, b int) int", // 导出函数
|
||||
"var MaxSize = 1000", // 导出变量
|
||||
"const DefaultTimeout = 30", // 导出常量
|
||||
"type User struct { Name string }", // 导出类型
|
||||
"func (u User) GetName() string", // 导出方法
|
||||
}
|
||||
|
||||
for _, example := range exportedExamples {
|
||||
fmt.Printf(" %s // 导出\\n", example)
|
||||
}
|
||||
|
||||
// 未导出标识符示例
|
||||
fmt.Printf(" 未导出标识符示例:\\n")
|
||||
unexportedExamples := []string{
|
||||
"func add(a, b int) int", // 未导出函数
|
||||
"var maxSize = 1000", // 未导出变量
|
||||
"const defaultTimeout = 30", // 未导出常量
|
||||
"type user struct { name string }", // 未导出类型
|
||||
"func (u user) getName() string", // 未导出方法
|
||||
}
|
||||
|
||||
for _, example := range unexportedExamples {
|
||||
fmt.Printf(" %s // 未导出\\n", example)
|
||||
}
|
||||
|
||||
// 结构体字段的可见性
|
||||
fmt.Printf(" 结构体字段的可见性:\\n")
|
||||
fmt.Printf(" type User struct {\\n")
|
||||
fmt.Printf(" Name string // 导出字段\\n")
|
||||
fmt.Printf(" Email string // 导出字段\\n")
|
||||
fmt.Printf(" age int // 未导出字段\\n")
|
||||
fmt.Printf(" address string // 未导出字段\\n")
|
||||
fmt.Printf(" }\\n")
|
||||
|
||||
// 方法的可见性
|
||||
fmt.Printf(" 方法的可见性:\\n")
|
||||
fmt.Printf(" func (u *User) GetName() string { // 导出方法\\n")
|
||||
fmt.Printf(" return u.Name\\n")
|
||||
fmt.Printf(" }\\n")
|
||||
fmt.Printf("\\n")
|
||||
fmt.Printf(" func (u *User) setAge(age int) { // 未导出方法\\n")
|
||||
fmt.Printf(" u.age = age\\n")
|
||||
fmt.Printf(" }\\n")
|
||||
|
||||
// 接口的可见性
|
||||
fmt.Printf(" 接口的可见性:\\n")
|
||||
fmt.Printf(" type Reader interface { // 导出接口\\n")
|
||||
fmt.Printf(" Read() ([]byte, error) // 导出方法\\n")
|
||||
fmt.Printf(" }\\n")
|
||||
fmt.Printf("\\n")
|
||||
fmt.Printf(" type writer interface { // 未导出接口\\n")
|
||||
fmt.Printf(" write([]byte) error // 未导出方法\\n")
|
||||
fmt.Printf(" }\\n")
|
||||
|
||||
// 可见性设计原则
|
||||
fmt.Printf(" 可见性设计原则:\\n")
|
||||
fmt.Printf(" 1. 最小暴露:只导出必要的标识符\\n")
|
||||
fmt.Printf(" 2. 稳定接口:导出的接口应该稳定\\n")
|
||||
fmt.Printf(" 3. 清晰命名:导出的标识符应该有清晰的名称\\n")
|
||||
fmt.Printf(" 4. 文档完整:导出的标识符应该有完整的文档\\n")
|
||||
fmt.Printf(" 5. 向后兼容:避免破坏性的接口变更\\n")
|
||||
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// demonstratePackageDocumentation 演示包的文档
|
||||
func demonstratePackageDocumentation() {
|
||||
fmt.Println("5. 包的文档:")
|
||||
|
||||
// 文档注释规则
|
||||
fmt.Printf(" 文档注释规则:\\n")
|
||||
fmt.Printf(" 1. 紧邻声明之前,中间不能有空行\\n")
|
||||
fmt.Printf(" 2. 以被注释的标识符名称开头\\n")
|
||||
fmt.Printf(" 3. 使用完整的句子\\n")
|
||||
fmt.Printf(" 4. 第一句话应该是简洁的摘要\\n")
|
||||
fmt.Printf(" 5. 可以包含示例代码\\n")
|
||||
|
||||
// 包文档示例
|
||||
fmt.Printf(" 包文档示例:\\n")
|
||||
fmt.Printf(" /*\\n")
|
||||
fmt.Printf(" Package math 提供基本的数学函数和常量。\\n")
|
||||
fmt.Printf("\\n")
|
||||
fmt.Printf(" 这个包包含了常用的数学运算函数,如三角函数、\\n")
|
||||
fmt.Printf(" 对数函数、指数函数等。所有函数都经过优化,\\n")
|
||||
fmt.Printf(" 适用于高性能计算场景。\\n")
|
||||
fmt.Printf("\\n")
|
||||
fmt.Printf(" 示例用法:\\n")
|
||||
fmt.Printf(" result := math.Sqrt(16) // 计算平方根\\n")
|
||||
fmt.Printf(" angle := math.Sin(math.Pi / 2) // 计算正弦值\\n")
|
||||
fmt.Printf(" */\\n")
|
||||
fmt.Printf(" package math\\n")
|
||||
|
||||
// 函数文档示例
|
||||
fmt.Printf(" 函数文档示例:\\n")
|
||||
fmt.Printf(" // Add 计算两个整数的和。\\n")
|
||||
fmt.Printf(" //\\n")
|
||||
fmt.Printf(" // 参数 a 和 b 可以是任意整数,包括负数。\\n")
|
||||
fmt.Printf(" // 返回值是 a 和 b 的算术和。\\n")
|
||||
fmt.Printf(" //\\n")
|
||||
fmt.Printf(" // 示例:\\n")
|
||||
fmt.Printf(" // sum := Add(3, 4) // 返回 7\\n")
|
||||
fmt.Printf(" // sum := Add(-1, 1) // 返回 0\\n")
|
||||
fmt.Printf(" func Add(a, b int) int {\\n")
|
||||
fmt.Printf(" return a + b\\n")
|
||||
fmt.Printf(" }\\n")
|
||||
|
||||
// 类型文档示例
|
||||
fmt.Printf(" 类型文档示例:\\n")
|
||||
fmt.Printf(" // User 表示系统中的用户。\\n")
|
||||
fmt.Printf(" //\\n")
|
||||
fmt.Printf(" // User 包含用户的基本信息,如姓名、邮箱等。\\n")
|
||||
fmt.Printf(" // 所有字段都是必需的,不能为空。\\n")
|
||||
fmt.Printf(" type User struct {\\n")
|
||||
fmt.Printf(" // Name 是用户的全名\\n")
|
||||
fmt.Printf(" Name string\\n")
|
||||
fmt.Printf("\\n")
|
||||
fmt.Printf(" // Email 是用户的邮箱地址,必须是有效格式\\n")
|
||||
fmt.Printf(" Email string\\n")
|
||||
fmt.Printf(" }\\n")
|
||||
|
||||
// 常量和变量文档
|
||||
fmt.Printf(" 常量和变量文档示例:\\n")
|
||||
fmt.Printf(" // MaxRetries 是操作失败时的最大重试次数。\\n")
|
||||
fmt.Printf(" const MaxRetries = 3\\n")
|
||||
fmt.Printf("\\n")
|
||||
fmt.Printf(" // DefaultTimeout 是网络操作的默认超时时间。\\n")
|
||||
fmt.Printf(" var DefaultTimeout = 30 * time.Second\\n")
|
||||
|
||||
// 文档生成
|
||||
fmt.Printf(" 文档生成:\\n")
|
||||
fmt.Printf(" - 使用 go doc 命令查看文档\\n")
|
||||
fmt.Printf(" - 使用 godoc 工具生成 HTML 文档\\n")
|
||||
fmt.Printf(" - 文档会自动发布到 pkg.go.dev\\n")
|
||||
|
||||
// 文档示例命令
|
||||
fmt.Printf(" 文档查看命令:\\n")
|
||||
fmt.Printf(" go doc utils // 查看包文档\\n")
|
||||
fmt.Printf(" go doc utils.Add // 查看函数文档\\n")
|
||||
fmt.Printf(" go doc -all utils // 查看所有文档\\n")
|
||||
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// demonstratePackageInitialization 演示包的初始化
|
||||
func demonstratePackageInitialization() {
|
||||
fmt.Println("6. 包的初始化:")
|
||||
|
||||
// 初始化顺序
|
||||
fmt.Printf(" 包初始化顺序:\\n")
|
||||
fmt.Printf(" 1. 导入的包先初始化\\n")
|
||||
fmt.Printf(" 2. 包级别变量按声明顺序初始化\\n")
|
||||
fmt.Printf(" 3. init 函数按出现顺序执行\\n")
|
||||
fmt.Printf(" 4. main 函数最后执行\\n")
|
||||
|
||||
// init 函数特点
|
||||
fmt.Printf(" init 函数特点:\\n")
|
||||
fmt.Printf(" - 函数名必须是 init\\n")
|
||||
fmt.Printf(" - 不能有参数和返回值\\n")
|
||||
fmt.Printf(" - 不能被显式调用\\n")
|
||||
fmt.Printf(" - 一个包可以有多个 init 函数\\n")
|
||||
fmt.Printf(" - 在包被导入时自动执行\\n")
|
||||
|
||||
// init 函数示例
|
||||
fmt.Printf(" init 函数示例:\\n")
|
||||
fmt.Printf(" package config\\n")
|
||||
fmt.Printf("\\n")
|
||||
fmt.Printf(" import (\\n")
|
||||
fmt.Printf(" \\\"fmt\\\"\\n")
|
||||
fmt.Printf(" \\\"os\\\"\\n")
|
||||
fmt.Printf(" )\\n")
|
||||
fmt.Printf("\\n")
|
||||
fmt.Printf(" var AppName string\\n")
|
||||
fmt.Printf("\\n")
|
||||
fmt.Printf(" func init() {\\n")
|
||||
fmt.Printf(" fmt.Println(\\\"初始化配置包\\\")\\n")
|
||||
fmt.Printf(" AppName = os.Getenv(\\\"APP_NAME\\\")\\n")
|
||||
fmt.Printf(" if AppName == \\\"\\\" {\\n")
|
||||
fmt.Printf(" AppName = \\\"DefaultApp\\\"\\n")
|
||||
fmt.Printf(" }\\n")
|
||||
fmt.Printf(" }\\n")
|
||||
|
||||
// 包级别变量初始化
|
||||
fmt.Printf(" 包级别变量初始化:\\n")
|
||||
fmt.Printf(" var (\\n")
|
||||
fmt.Printf(" // 简单初始化\\n")
|
||||
fmt.Printf(" MaxSize = 1000\\n")
|
||||
fmt.Printf("\\n")
|
||||
fmt.Printf(" // 函数调用初始化\\n")
|
||||
fmt.Printf(" StartTime = time.Now()\\n")
|
||||
fmt.Printf("\\n")
|
||||
fmt.Printf(" // 复杂初始化\\n")
|
||||
fmt.Printf(" Config = loadConfig()\\n")
|
||||
fmt.Printf(" )\\n")
|
||||
|
||||
// 初始化依赖
|
||||
fmt.Printf(" 初始化依赖:\\n")
|
||||
fmt.Printf(" - Go 会自动分析变量间的依赖关系\\n")
|
||||
fmt.Printf(" - 按依赖顺序进行初始化\\n")
|
||||
fmt.Printf(" - 循环依赖会导致编译错误\\n")
|
||||
|
||||
// 初始化示例
|
||||
fmt.Printf(" 初始化示例:\\n")
|
||||
fmt.Printf(" var (\\n")
|
||||
fmt.Printf(" a = b + 1 // 依赖 b\\n")
|
||||
fmt.Printf(" b = 2 // 先初始化\\n")
|
||||
fmt.Printf(" c = a + b // 依赖 a 和 b\\n")
|
||||
fmt.Printf(" )\\n")
|
||||
fmt.Printf(" // 初始化顺序:b -> a -> c\\n")
|
||||
|
||||
// 初始化最佳实践
|
||||
fmt.Printf(" 初始化最佳实践:\\n")
|
||||
fmt.Printf(" 1. 保持 init 函数简单\\n")
|
||||
fmt.Printf(" 2. 避免在 init 中进行复杂操作\\n")
|
||||
fmt.Printf(" 3. 不要依赖 init 函数的执行顺序\\n")
|
||||
fmt.Printf(" 4. 优先使用包级别变量初始化\\n")
|
||||
fmt.Printf(" 5. 处理初始化可能的错误\\n")
|
||||
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// demonstratePracticalPackageCreation 演示实际包创建示例
|
||||
func demonstratePracticalPackageCreation() {
|
||||
fmt.Println("7. 实际包创建示例:")
|
||||
|
||||
// 分析现有包结构
|
||||
fmt.Printf(" 分析现有包结构:\\n")
|
||||
|
||||
// 获取当前目录
|
||||
currentDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
fmt.Printf(" 获取当前目录失败: %v\\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 分析 utils 包
|
||||
utilsPath := filepath.Join(currentDir, "utils")
|
||||
if _, err := os.Stat(utilsPath); err == nil {
|
||||
fmt.Printf(" 找到 utils 包: %s\\n", utilsPath)
|
||||
analyzePackage(utilsPath)
|
||||
} else {
|
||||
fmt.Printf(" utils 包不存在: %v\\n", err)
|
||||
}
|
||||
|
||||
// 包创建步骤
|
||||
fmt.Printf(" 包创建步骤:\\n")
|
||||
fmt.Printf(" 1. 创建包目录\\n")
|
||||
fmt.Printf(" 2. 编写包声明\\n")
|
||||
fmt.Printf(" 3. 定义导出的接口\\n")
|
||||
fmt.Printf(" 4. 实现功能函数\\n")
|
||||
fmt.Printf(" 5. 添加文档注释\\n")
|
||||
fmt.Printf(" 6. 编写测试文件\\n")
|
||||
fmt.Printf(" 7. 创建示例代码\\n")
|
||||
|
||||
// 包设计原则
|
||||
fmt.Printf(" 包设计原则:\\n")
|
||||
fmt.Printf(" 1. 单一职责:每个包应该有明确的职责\\n")
|
||||
fmt.Printf(" 2. 高内聚:包内元素应该紧密相关\\n")
|
||||
fmt.Printf(" 3. 低耦合:包间依赖应该最小化\\n")
|
||||
fmt.Printf(" 4. 稳定接口:导出接口应该稳定\\n")
|
||||
fmt.Printf(" 5. 易于使用:API 应该简单直观\\n")
|
||||
|
||||
// 包的版本管理
|
||||
fmt.Printf(" 包的版本管理:\\n")
|
||||
fmt.Printf(" - 使用语义化版本号(如 v1.2.3)\\n")
|
||||
fmt.Printf(" - 主版本号:不兼容的 API 变更\\n")
|
||||
fmt.Printf(" - 次版本号:向后兼容的功能增加\\n")
|
||||
fmt.Printf(" - 修订版本号:向后兼容的问题修复\\n")
|
||||
|
||||
// 包的发布
|
||||
fmt.Printf(" 包的发布:\\n")
|
||||
fmt.Printf(" 1. 创建 Git 仓库\\n")
|
||||
fmt.Printf(" 2. 添加 go.mod 文件\\n")
|
||||
fmt.Printf(" 3. 编写 README.md\\n")
|
||||
fmt.Printf(" 4. 添加许可证文件\\n")
|
||||
fmt.Printf(" 5. 创建版本标签\\n")
|
||||
fmt.Printf(" 6. 推送到代码仓库\\n")
|
||||
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// analyzePackage 分析包的结构
|
||||
func analyzePackage(packagePath string) {
|
||||
fmt.Printf(" 分析包: %s\\n", packagePath)
|
||||
|
||||
// 遍历包目录中的 Go 文件
|
||||
err := filepath.Walk(packagePath, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 只处理 .go 文件
|
||||
if !strings.HasSuffix(path, ".go") {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 解析 Go 文件
|
||||
fset := token.NewFileSet()
|
||||
node, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
fmt.Printf(" 解析文件 %s 失败: %v\\n", path, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 分析文件内容
|
||||
fmt.Printf(" 文件: %s\\n", filepath.Base(path))
|
||||
fmt.Printf(" 包名: %s\\n", node.Name.Name)
|
||||
|
||||
// 统计导出和未导出的标识符
|
||||
var exportedFuncs, unexportedFuncs int
|
||||
var exportedTypes, unexportedTypes int
|
||||
var exportedVars, unexportedVars int
|
||||
|
||||
for _, decl := range node.Decls {
|
||||
switch d := decl.(type) {
|
||||
case *ast.FuncDecl:
|
||||
if d.Name.IsExported() {
|
||||
exportedFuncs++
|
||||
} else {
|
||||
unexportedFuncs++
|
||||
}
|
||||
case *ast.GenDecl:
|
||||
for _, spec := range d.Specs {
|
||||
switch s := spec.(type) {
|
||||
case *ast.TypeSpec:
|
||||
if s.Name.IsExported() {
|
||||
exportedTypes++
|
||||
} else {
|
||||
unexportedTypes++
|
||||
}
|
||||
case *ast.ValueSpec:
|
||||
for _, name := range s.Names {
|
||||
if name.IsExported() {
|
||||
exportedVars++
|
||||
} else {
|
||||
unexportedVars++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf(" 导出函数: %d, 未导出函数: %d\\n", exportedFuncs, unexportedFuncs)
|
||||
fmt.Printf(" 导出类型: %d, 未导出类型: %d\\n", exportedTypes, unexportedTypes)
|
||||
fmt.Printf(" 导出变量: %d, 未导出变量: %d\\n", exportedVars, unexportedVars)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf(" 分析包失败: %v\\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
运行这个程序:
|
||||
go run 01-creating-packages.go
|
||||
|
||||
学习要点:
|
||||
1. 包是 Go 语言组织代码的基本单位
|
||||
2. 包提供了命名空间和封装机制
|
||||
3. 包的可见性通过标识符首字母大小写控制
|
||||
4. 包应该有清晰的文档和合理的组织结构
|
||||
5. 包的设计应该遵循单一职责和高内聚低耦合原则
|
||||
|
||||
包的基本概念:
|
||||
1. 每个 Go 文件都必须属于一个包
|
||||
2. 同一目录下的所有 .go 文件必须属于同一个包
|
||||
3. 包名通常与目录名相同
|
||||
4. main 包是程序的入口点
|
||||
5. 包提供了代码的模块化和重用机制
|
||||
|
||||
包的组织原则:
|
||||
1. 按功能分组:相关功能放在同一个包中
|
||||
2. 单一职责:每个包应该有明确的职责
|
||||
3. 依赖管理:避免循环依赖
|
||||
4. 接口分离:定义清晰的包接口
|
||||
5. 层次结构:建立合理的包层次
|
||||
|
||||
可见性规则:
|
||||
1. 首字母大写:导出(公开),可被其他包访问
|
||||
2. 首字母小写:未导出(私有),只能包内访问
|
||||
3. 适用于函数、变量、常量、类型、方法、字段
|
||||
4. 最小暴露原则:只导出必要的标识符
|
||||
5. 稳定接口:导出的接口应该稳定
|
||||
|
||||
包的文档:
|
||||
1. 文档注释紧邻声明之前
|
||||
2. 以被注释的标识符名称开头
|
||||
3. 使用完整的句子
|
||||
4. 第一句话应该是简洁的摘要
|
||||
5. 可以包含示例代码
|
||||
|
||||
包的初始化:
|
||||
1. 导入的包先初始化
|
||||
2. 包级别变量按依赖顺序初始化
|
||||
3. init 函数按出现顺序执行
|
||||
4. main 函数最后执行
|
||||
5. 每个包只初始化一次
|
||||
|
||||
最佳实践:
|
||||
1. 使用清晰简洁的包名
|
||||
2. 遵循 Go 的命名规范
|
||||
3. 编写完整的文档注释
|
||||
4. 保持包的职责单一
|
||||
5. 设计稳定的公共接口
|
||||
6. 避免循环依赖
|
||||
7. 合理组织包的结构
|
||||
|
||||
注意事项:
|
||||
1. 包名应该小写且简洁
|
||||
2. 避免使用通用名称如 util、common
|
||||
3. 不要在包名中使用下划线
|
||||
4. 包的导入路径应该唯一
|
||||
5. 考虑包的向后兼容性
|
||||
*/
|
579
golang-learning/08-packages/02-importing-packages.go
Normal file
579
golang-learning/08-packages/02-importing-packages.go
Normal file
@@ -0,0 +1,579 @@
|
||||
/*
|
||||
02-importing-packages.go - Go 语言包导入详解
|
||||
|
||||
学习目标:
|
||||
1. 掌握包的导入语法和方式
|
||||
2. 了解不同的导入模式
|
||||
3. 学会处理包的别名和冲突
|
||||
4. 理解包的初始化顺序
|
||||
5. 掌握包导入的最佳实践
|
||||
|
||||
知识点:
|
||||
- 包的导入语法
|
||||
- 导入别名和点导入
|
||||
- 空白导入
|
||||
- 包的初始化顺序
|
||||
- 循环导入问题
|
||||
- 包管理工具的使用
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
// 标准库导入
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
|
||||
// 本地包导入
|
||||
"./utils" // 相对路径导入(不推荐)
|
||||
|
||||
// 别名导入
|
||||
myutils "./utils"
|
||||
|
||||
// 点导入(不推荐)
|
||||
. "fmt"
|
||||
|
||||
// 空白导入
|
||||
_ "net/http/pprof"
|
||||
)
|
||||
|
||||
func main() {
|
||||
Println("=== Go 语言包导入详解 ===\n") // 使用点导入的 fmt.Println
|
||||
|
||||
// 演示基本导入
|
||||
demonstrateBasicImports()
|
||||
|
||||
// 演示导入别名
|
||||
demonstrateImportAliases()
|
||||
|
||||
// 演示点导入
|
||||
demonstrateDotImports()
|
||||
|
||||
// 演示空白导入
|
||||
demonstrateBlankImports()
|
||||
|
||||
// 演示包的初始化顺序
|
||||
demonstrateInitializationOrder()
|
||||
|
||||
// 演示循环导入问题
|
||||
demonstrateCircularImports()
|
||||
|
||||
// 演示包管理
|
||||
demonstratePackageManagement()
|
||||
}
|
||||
|
||||
// demonstrateBasicImports 演示基本导入
|
||||
func demonstrateBasicImports() {
|
||||
fmt.Println("1. 基本导入:")
|
||||
|
||||
// 导入语法
|
||||
fmt.Printf(" 导入语法:\n")
|
||||
fmt.Printf(" import \"package-path\" // 单个导入\n")
|
||||
fmt.Printf(" import ( // 多个导入\n")
|
||||
fmt.Printf(" \"fmt\"\n")
|
||||
fmt.Printf(" \"time\"\n")
|
||||
fmt.Printf(" \"strings\"\n")
|
||||
fmt.Printf(" )\n")
|
||||
|
||||
// 标准库导入示例
|
||||
fmt.Printf(" 标准库导入示例:\n")
|
||||
|
||||
// 使用 time 包
|
||||
now := time.Now()
|
||||
fmt.Printf(" 当前时间: %s\n", now.Format("2006-01-02 15:04:05"))
|
||||
|
||||
// 使用 strings 包
|
||||
text := "Hello, World!"
|
||||
upper := strings.ToUpper(text)
|
||||
fmt.Printf(" 大写转换: %s -> %s\n", text, upper)
|
||||
|
||||
// 使用 json 包
|
||||
data := map[string]interface{}{
|
||||
"name": "Alice",
|
||||
"age": 30,
|
||||
"city": "Beijing",
|
||||
}
|
||||
|
||||
jsonData, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
fmt.Printf(" JSON 编码失败: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf(" JSON 编码: %s\n", string(jsonData))
|
||||
}
|
||||
|
||||
// 本地包导入示例
|
||||
fmt.Printf(" 本地包导入示例:\n")
|
||||
|
||||
// 使用 utils 包
|
||||
sum := utils.Add(10, 20)
|
||||
fmt.Printf(" utils.Add(10, 20) = %d\n", sum)
|
||||
|
||||
product := utils.Multiply(5, 6)
|
||||
fmt.Printf(" utils.Multiply(5, 6) = %d\n", product)
|
||||
|
||||
greeting := utils.GetGreeting("Go Developer")
|
||||
fmt.Printf(" utils.GetGreeting(\"Go Developer\") = %s\n", greeting)
|
||||
|
||||
// 包的版本信息
|
||||
fmt.Printf(" utils 包版本: %s\n", utils.PackageVersion)
|
||||
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// demonstrateImportAliases 演示导入别名
|
||||
func demonstrateImportAliases() {
|
||||
fmt.Println("2. 导入别名:")
|
||||
|
||||
// 别名导入的用途
|
||||
fmt.Printf(" 别名导入的用途:\n")
|
||||
fmt.Printf(" 1. 解决包名冲突\n")
|
||||
fmt.Printf(" 2. 简化长包名\n")
|
||||
fmt.Printf(" 3. 提高代码可读性\n")
|
||||
fmt.Printf(" 4. 避免与本地标识符冲突\n")
|
||||
|
||||
// 别名导入语法
|
||||
fmt.Printf(" 别名导入语法:\n")
|
||||
fmt.Printf(" import alias \"package-path\"\n")
|
||||
fmt.Printf(" import (\n")
|
||||
fmt.Printf(" myutils \"./utils\"\n")
|
||||
fmt.Printf(" httputil \"net/http/httputil\"\n")
|
||||
fmt.Printf(" )\n")
|
||||
|
||||
// 使用别名导入的包
|
||||
fmt.Printf(" 使用别名导入的包:\n")
|
||||
|
||||
// 使用 myutils 别名
|
||||
result := myutils.Add(15, 25)
|
||||
fmt.Printf(" myutils.Add(15, 25) = %d\n", result)
|
||||
|
||||
maxVal := myutils.Max(10, 20)
|
||||
fmt.Printf(" myutils.Max(10, 20) = %d\n", maxVal)
|
||||
|
||||
isEven := myutils.IsEven(42)
|
||||
fmt.Printf(" myutils.IsEven(42) = %t\n", isEven)
|
||||
|
||||
// 常见的别名使用场景
|
||||
fmt.Printf(" 常见的别名使用场景:\n")
|
||||
|
||||
// 场景1:解决包名冲突
|
||||
fmt.Printf(" 场景1 - 解决包名冲突:\n")
|
||||
fmt.Printf(" import (\n")
|
||||
fmt.Printf(" \"crypto/rand\"\n")
|
||||
fmt.Printf(" mathrand \"math/rand\"\n")
|
||||
fmt.Printf(" )\n")
|
||||
fmt.Printf(" // 使用: rand.Read() 和 mathrand.Intn()\n")
|
||||
|
||||
// 场景2:简化长包名
|
||||
fmt.Printf(" 场景2 - 简化长包名:\n")
|
||||
fmt.Printf(" import pb \"github.com/company/project/proto/user/v1\"\n")
|
||||
fmt.Printf(" // 使用: pb.User{} 而不是 user_v1.User{}\n")
|
||||
|
||||
// 场景3:版本管理
|
||||
fmt.Printf(" 场景3 - 版本管理:\n")
|
||||
fmt.Printf(" import (\n")
|
||||
fmt.Printf(" v1 \"myproject/api/v1\"\n")
|
||||
fmt.Printf(" v2 \"myproject/api/v2\"\n")
|
||||
fmt.Printf(" )\n")
|
||||
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// demonstrateDotImports 演示点导入
|
||||
func demonstrateDotImports() {
|
||||
fmt.Println("3. 点导入:")
|
||||
|
||||
// 点导入的概念
|
||||
fmt.Printf(" 点导入的概念:\n")
|
||||
fmt.Printf(" - 使用 . 作为包别名\n")
|
||||
fmt.Printf(" - 导入包的导出标识符到当前包的命名空间\n")
|
||||
fmt.Printf(" - 可以直接使用导入包的函数,无需包名前缀\n")
|
||||
fmt.Printf(" - 一般不推荐使用,容易造成命名冲突\n")
|
||||
|
||||
// 点导入语法
|
||||
fmt.Printf(" 点导入语法:\n")
|
||||
fmt.Printf(" import . \"fmt\"\n")
|
||||
fmt.Printf(" import . \"math\"\n")
|
||||
fmt.Printf(" // 使用: Println() 而不是 fmt.Println()\n")
|
||||
|
||||
// 点导入示例
|
||||
fmt.Printf(" 点导入示例:\n")
|
||||
|
||||
// 直接使用 fmt 包的函数(通过点导入)
|
||||
Printf(" 使用点导入的 Printf 函数\n")
|
||||
Sprintf("格式化字符串") // 这里不会输出,只是演示语法
|
||||
|
||||
// 点导入的问题
|
||||
fmt.Printf(" 点导入的问题:\n")
|
||||
fmt.Printf(" 1. 命名冲突:可能与本地标识符冲突\n")
|
||||
fmt.Printf(" 2. 可读性差:不清楚函数来自哪个包\n")
|
||||
fmt.Printf(" 3. 维护困难:重构时难以追踪依赖\n")
|
||||
fmt.Printf(" 4. 测试复杂:难以进行包级别的测试\n")
|
||||
|
||||
// 点导入的适用场景
|
||||
fmt.Printf(" 点导入的适用场景:\n")
|
||||
fmt.Printf(" 1. 测试文件:import . \"testing\"\n")
|
||||
fmt.Printf(" 2. DSL 实现:领域特定语言\n")
|
||||
fmt.Printf(" 3. 数学计算:import . \"math\"\n")
|
||||
fmt.Printf(" 4. 临时脚本:快速原型开发\n")
|
||||
|
||||
// 替代方案
|
||||
fmt.Printf(" 替代方案:\n")
|
||||
fmt.Printf(" 1. 使用短别名:import f \"fmt\"\n")
|
||||
fmt.Printf(" 2. 局部变量:var printf = fmt.Printf\n")
|
||||
fmt.Printf(" 3. 包装函数:func print(args ...interface{}) { fmt.Print(args...) }\n")
|
||||
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// demonstrateBlankImports 演示空白导入
|
||||
func demonstrateBlankImports() {
|
||||
fmt.Println("4. 空白导入:")
|
||||
|
||||
// 空白导入的概念
|
||||
fmt.Printf(" 空白导入的概念:\n")
|
||||
fmt.Printf(" - 使用 _ 作为包别名\n")
|
||||
fmt.Printf(" - 只执行包的初始化,不使用包的导出标识符\n")
|
||||
fmt.Printf(" - 主要用于触发包的副作用\n")
|
||||
fmt.Printf(" - 避免编译器的未使用导入错误\n")
|
||||
|
||||
// 空白导入语法
|
||||
fmt.Printf(" 空白导入语法:\n")
|
||||
fmt.Printf(" import _ \"package-path\"\n")
|
||||
fmt.Printf(" import (\n")
|
||||
fmt.Printf(" _ \"net/http/pprof\" // 注册 pprof 处理器\n")
|
||||
fmt.Printf(" _ \"image/jpeg\" // 注册 JPEG 解码器\n")
|
||||
fmt.Printf(" )\n")
|
||||
|
||||
// 空白导入的用途
|
||||
fmt.Printf(" 空白导入的用途:\n")
|
||||
fmt.Printf(" 1. 数据库驱动注册\n")
|
||||
fmt.Printf(" 2. 图像格式支持\n")
|
||||
fmt.Printf(" 3. 插件系统\n")
|
||||
fmt.Printf(" 4. 性能分析工具\n")
|
||||
fmt.Printf(" 5. 静态资源嵌入\n")
|
||||
|
||||
// 常见的空白导入示例
|
||||
fmt.Printf(" 常见的空白导入示例:\n")
|
||||
|
||||
// 1. 数据库驱动
|
||||
fmt.Printf(" 1. 数据库驱动:\n")
|
||||
fmt.Printf(" import (\n")
|
||||
fmt.Printf(" \"database/sql\"\n")
|
||||
fmt.Printf(" _ \"github.com/go-sql-driver/mysql\" // MySQL 驱动\n")
|
||||
fmt.Printf(" _ \"github.com/lib/pq\" // PostgreSQL 驱动\n")
|
||||
fmt.Printf(" )\n")
|
||||
|
||||
// 2. 图像格式支持
|
||||
fmt.Printf(" 2. 图像格式支持:\n")
|
||||
fmt.Printf(" import (\n")
|
||||
fmt.Printf(" \"image\"\n")
|
||||
fmt.Printf(" _ \"image/jpeg\" // JPEG 支持\n")
|
||||
fmt.Printf(" _ \"image/png\" // PNG 支持\n")
|
||||
fmt.Printf(" _ \"image/gif\" // GIF 支持\n")
|
||||
fmt.Printf(" )\n")
|
||||
|
||||
// 3. HTTP 性能分析
|
||||
fmt.Printf(" 3. HTTP 性能分析:\n")
|
||||
fmt.Printf(" import _ \"net/http/pprof\"\n")
|
||||
fmt.Printf(" // 自动注册 /debug/pprof/ 路由\n")
|
||||
|
||||
// 空白导入的注意事项
|
||||
fmt.Printf(" 空白导入的注意事项:\n")
|
||||
fmt.Printf(" 1. 增加编译时间和二进制大小\n")
|
||||
fmt.Printf(" 2. 可能引入不需要的依赖\n")
|
||||
fmt.Printf(" 3. 初始化顺序可能影响程序行为\n")
|
||||
fmt.Printf(" 4. 难以追踪包的实际用途\n")
|
||||
|
||||
// 检查空白导入的效果
|
||||
fmt.Printf(" 检查空白导入的效果:\n")
|
||||
fmt.Printf(" net/http/pprof 包已通过空白导入加载\n")
|
||||
fmt.Printf(" 可以通过 http://localhost:6060/debug/pprof/ 访问性能分析\n")
|
||||
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// demonstrateInitializationOrder 演示包的初始化顺序
|
||||
func demonstrateInitializationOrder() {
|
||||
fmt.Println("5. 包的初始化顺序:")
|
||||
|
||||
// 初始化顺序规则
|
||||
fmt.Printf(" 初始化顺序规则:\n")
|
||||
fmt.Printf(" 1. 依赖包先初始化\n")
|
||||
fmt.Printf(" 2. 同一包内按依赖关系初始化变量\n")
|
||||
fmt.Printf(" 3. init 函数按文件名字典序执行\n")
|
||||
fmt.Printf(" 4. 同一文件内 init 函数按出现顺序执行\n")
|
||||
fmt.Printf(" 5. main 函数最后执行\n")
|
||||
|
||||
// 初始化示例
|
||||
fmt.Printf(" 初始化示例:\n")
|
||||
fmt.Printf(" 包 A 导入 包 B 和 包 C\n")
|
||||
fmt.Printf(" 包 B 导入 包 D\n")
|
||||
fmt.Printf(" 包 C 导入 包 D\n")
|
||||
fmt.Printf("\\n")
|
||||
fmt.Printf(" 初始化顺序: D -> B -> C -> A\n")
|
||||
|
||||
// 变量初始化顺序
|
||||
fmt.Printf(" 变量初始化顺序:\n")
|
||||
fmt.Printf(" var (\n")
|
||||
fmt.Printf(" a = b + 1 // 第二个初始化\n")
|
||||
fmt.Printf(" b = 2 // 第一个初始化\n")
|
||||
fmt.Printf(" c = a + b // 第三个初始化\n")
|
||||
fmt.Printf(" )\n")
|
||||
|
||||
// init 函数执行顺序
|
||||
fmt.Printf(" init 函数执行顺序:\n")
|
||||
fmt.Printf(" 文件 a.go:\n")
|
||||
fmt.Printf(" func init() { fmt.Println(\"a.go init 1\") }\n")
|
||||
fmt.Printf(" func init() { fmt.Println(\"a.go init 2\") }\n")
|
||||
fmt.Printf("\\n")
|
||||
fmt.Printf(" 文件 b.go:\n")
|
||||
fmt.Printf(" func init() { fmt.Println(\"b.go init 1\") }\n")
|
||||
fmt.Printf("\\n")
|
||||
fmt.Printf(" 执行顺序: a.go init 1 -> a.go init 2 -> b.go init 1\n")
|
||||
|
||||
// 实际初始化演示
|
||||
fmt.Printf(" 实际初始化演示:\n")
|
||||
fmt.Printf(" utils 包的初始化信息:\n")
|
||||
fmt.Printf(" 版本: %s\n", utils.Version)
|
||||
fmt.Printf(" 最大重试次数: %d\n", utils.MaxRetries)
|
||||
|
||||
// 初始化最佳实践
|
||||
fmt.Printf(" 初始化最佳实践:\n")
|
||||
fmt.Printf(" 1. 保持 init 函数简单\n")
|
||||
fmt.Printf(" 2. 避免复杂的初始化逻辑\n")
|
||||
fmt.Printf(" 3. 不要依赖 init 函数的执行顺序\n")
|
||||
fmt.Printf(" 4. 处理初始化可能的错误\n")
|
||||
fmt.Printf(" 5. 使用延迟初始化处理复杂情况\n")
|
||||
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// demonstrateCircularImports 演示循环导入问题
|
||||
func demonstrateCircularImports() {
|
||||
fmt.Println("6. 循环导入问题:")
|
||||
|
||||
// 循环导入的概念
|
||||
fmt.Printf(" 循环导入的概念:\n")
|
||||
fmt.Printf(" - 两个或多个包相互导入\n")
|
||||
fmt.Printf(" - Go 编译器禁止循环导入\n")
|
||||
fmt.Printf(" - 会导致编译错误\n")
|
||||
fmt.Printf(" - 需要重新设计包结构来解决\n")
|
||||
|
||||
// 循环导入示例
|
||||
fmt.Printf(" 循环导入示例:\n")
|
||||
fmt.Printf(" 包 A:\n")
|
||||
fmt.Printf(" package a\n")
|
||||
fmt.Printf(" import \"myproject/b\"\n")
|
||||
fmt.Printf(" func UseB() { b.FuncB() }\n")
|
||||
fmt.Printf("\\n")
|
||||
fmt.Printf(" 包 B:\n")
|
||||
fmt.Printf(" package b\n")
|
||||
fmt.Printf(" import \"myproject/a\" // 循环导入!\n")
|
||||
fmt.Printf(" func UseA() { a.FuncA() }\n")
|
||||
|
||||
// 循环导入的检测
|
||||
fmt.Printf(" 循环导入的检测:\n")
|
||||
fmt.Printf(" 编译错误信息:\n")
|
||||
fmt.Printf(" import cycle not allowed\n")
|
||||
fmt.Printf(" package myproject/a\n")
|
||||
fmt.Printf(" imports myproject/b\n")
|
||||
fmt.Printf(" imports myproject/a\n")
|
||||
|
||||
// 解决循环导入的方法
|
||||
fmt.Printf(" 解决循环导入的方法:\n")
|
||||
fmt.Printf(" 1. 提取公共接口\n")
|
||||
fmt.Printf(" 2. 创建中间包\n")
|
||||
fmt.Printf(" 3. 重新组织包结构\n")
|
||||
fmt.Printf(" 4. 使用依赖注入\n")
|
||||
fmt.Printf(" 5. 合并相关包\n")
|
||||
|
||||
// 解决方案示例
|
||||
fmt.Printf(" 解决方案示例:\n")
|
||||
fmt.Printf(" 方案1 - 提取公共接口:\n")
|
||||
fmt.Printf(" 包 interfaces:\n")
|
||||
fmt.Printf(" type ServiceA interface { MethodA() }\n")
|
||||
fmt.Printf(" type ServiceB interface { MethodB() }\n")
|
||||
fmt.Printf("\\n")
|
||||
fmt.Printf(" 包 A 导入 interfaces\n")
|
||||
fmt.Printf(" 包 B 导入 interfaces\n")
|
||||
fmt.Printf(" main 包导入 A 和 B,进行依赖注入\n")
|
||||
|
||||
fmt.Printf(" 方案2 - 创建中间包:\n")
|
||||
fmt.Printf(" 包 common: 包含 A 和 B 都需要的功能\n")
|
||||
fmt.Printf(" 包 A 导入 common\n")
|
||||
fmt.Printf(" 包 B 导入 common\n")
|
||||
fmt.Printf(" A 和 B 不再相互导入\n")
|
||||
|
||||
// 预防循环导入
|
||||
fmt.Printf(" 预防循环导入:\n")
|
||||
fmt.Printf(" 1. 设计清晰的包层次结构\n")
|
||||
fmt.Printf(" 2. 遵循依赖倒置原则\n")
|
||||
fmt.Printf(" 3. 使用接口定义包边界\n")
|
||||
fmt.Printf(" 4. 定期检查包依赖关系\n")
|
||||
fmt.Printf(" 5. 使用工具检测循环依赖\n")
|
||||
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// demonstratePackageManagement 演示包管理
|
||||
func demonstratePackageManagement() {
|
||||
fmt.Println("7. 包管理:")
|
||||
|
||||
// Go Modules 概念
|
||||
fmt.Printf(" Go Modules 概念:\n")
|
||||
fmt.Printf(" - Go 1.11+ 的官方包管理工具\n")
|
||||
fmt.Printf(" - 替代 GOPATH 模式\n")
|
||||
fmt.Printf(" - 支持版本管理和依赖解析\n")
|
||||
fmt.Printf(" - 使用 go.mod 文件定义模块\n")
|
||||
|
||||
// go.mod 文件示例
|
||||
fmt.Printf(" go.mod 文件示例:\n")
|
||||
fmt.Printf(" module myproject\n")
|
||||
fmt.Printf("\\n")
|
||||
fmt.Printf(" go 1.19\n")
|
||||
fmt.Printf("\\n")
|
||||
fmt.Printf(" require (\n")
|
||||
fmt.Printf(" github.com/gin-gonic/gin v1.9.1\n")
|
||||
fmt.Printf(" github.com/go-redis/redis/v8 v8.11.5\n")
|
||||
fmt.Printf(" )\n")
|
||||
fmt.Printf("\\n")
|
||||
fmt.Printf(" replace github.com/old/package => github.com/new/package v1.0.0\n")
|
||||
|
||||
// 常用的 go mod 命令
|
||||
fmt.Printf(" 常用的 go mod 命令:\n")
|
||||
commands := map[string]string{
|
||||
"go mod init <module>": "初始化新模块",
|
||||
"go mod tidy": "添加缺失的依赖,移除未使用的依赖",
|
||||
"go mod download": "下载依赖到本地缓存",
|
||||
"go mod verify": "验证依赖的完整性",
|
||||
"go mod graph": "打印模块依赖图",
|
||||
"go mod why <package>": "解释为什么需要某个包",
|
||||
"go mod edit": "编辑 go.mod 文件",
|
||||
"go mod vendor": "将依赖复制到 vendor 目录",
|
||||
}
|
||||
|
||||
for cmd, desc := range commands {
|
||||
fmt.Printf(" %-25s - %s\n", cmd, desc)
|
||||
}
|
||||
|
||||
// 版本管理
|
||||
fmt.Printf(" 版本管理:\n")
|
||||
fmt.Printf(" 语义化版本: v1.2.3\n")
|
||||
fmt.Printf(" 主版本号: 不兼容的 API 变更\n")
|
||||
fmt.Printf(" 次版本号: 向后兼容的功能增加\n")
|
||||
fmt.Printf(" 修订版本号: 向后兼容的问题修复\n")
|
||||
fmt.Printf("\\n")
|
||||
fmt.Printf(" 版本约束:\n")
|
||||
fmt.Printf(" v1.2.3 - 精确版本\n")
|
||||
fmt.Printf(" v1.2 - 最新的 v1.2.x\n")
|
||||
fmt.Printf(" v1 - 最新的 v1.x.x\n")
|
||||
fmt.Printf(" latest - 最新版本\n")
|
||||
|
||||
// 私有包管理
|
||||
fmt.Printf(" 私有包管理:\n")
|
||||
fmt.Printf(" 环境变量设置:\n")
|
||||
fmt.Printf(" GOPRIVATE=github.com/mycompany/*\n")
|
||||
fmt.Printf(" GONOPROXY=github.com/mycompany/*\n")
|
||||
fmt.Printf(" GONOSUMDB=github.com/mycompany/*\n")
|
||||
|
||||
// 检查当前项目的模块信息
|
||||
fmt.Printf(" 当前项目信息:\n")
|
||||
|
||||
// 查找 go.mod 文件
|
||||
currentDir, _ := os.Getwd()
|
||||
goModPath := filepath.Join(currentDir, "go.mod")
|
||||
|
||||
if _, err := os.Stat(goModPath); err == nil {
|
||||
content, err := ioutil.ReadFile(goModPath)
|
||||
if err == nil {
|
||||
fmt.Printf(" 找到 go.mod 文件:\n")
|
||||
lines := strings.Split(string(content), "\n")
|
||||
for i, line := range lines {
|
||||
if i < 10 { // 只显示前10行
|
||||
fmt.Printf(" %s\n", line)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fmt.Printf(" 当前目录没有 go.mod 文件\n")
|
||||
fmt.Printf(" 可以使用 'go mod init <module-name>' 初始化\n")
|
||||
}
|
||||
|
||||
// 包管理最佳实践
|
||||
fmt.Printf(" 包管理最佳实践:\n")
|
||||
fmt.Printf(" 1. 使用语义化版本号\n")
|
||||
fmt.Printf(" 2. 定期更新依赖\n")
|
||||
fmt.Printf(" 3. 锁定关键依赖的版本\n")
|
||||
fmt.Printf(" 4. 使用 go mod tidy 清理依赖\n")
|
||||
fmt.Printf(" 5. 提交 go.mod 和 go.sum 文件\n")
|
||||
fmt.Printf(" 6. 避免使用 replace 指令在生产环境\n")
|
||||
fmt.Printf(" 7. 定期检查安全漏洞\n")
|
||||
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
/*
|
||||
运行这个程序:
|
||||
go run 02-importing-packages.go
|
||||
|
||||
学习要点:
|
||||
1. Go 语言支持多种包导入方式
|
||||
2. 包的导入路径决定了包的唯一性
|
||||
3. 包的初始化有明确的顺序规则
|
||||
4. 循环导入是被禁止的,需要重新设计包结构
|
||||
5. Go Modules 是现代 Go 项目的标准包管理方式
|
||||
|
||||
包导入方式:
|
||||
1. 基本导入:import "package-path"
|
||||
2. 别名导入:import alias "package-path"
|
||||
3. 点导入:import . "package-path"(不推荐)
|
||||
4. 空白导入:import _ "package-path"
|
||||
|
||||
导入路径类型:
|
||||
1. 标准库:fmt, time, strings 等
|
||||
2. 相对路径:./utils, ../common(不推荐)
|
||||
3. 绝对路径:github.com/user/project/pkg
|
||||
4. 内部包:internal/ 目录下的包
|
||||
|
||||
包初始化顺序:
|
||||
1. 依赖包先初始化
|
||||
2. 包级别变量按依赖关系初始化
|
||||
3. init 函数按文件名和出现顺序执行
|
||||
4. main 函数最后执行
|
||||
|
||||
循环导入解决方案:
|
||||
1. 提取公共接口
|
||||
2. 创建中间包
|
||||
3. 重新组织包结构
|
||||
4. 使用依赖注入
|
||||
5. 合并相关包
|
||||
|
||||
Go Modules 特性:
|
||||
1. 版本管理:支持语义化版本
|
||||
2. 依赖解析:自动解决依赖冲突
|
||||
3. 模块缓存:提高构建速度
|
||||
4. 安全验证:校验包的完整性
|
||||
5. 私有包支持:支持私有仓库
|
||||
|
||||
最佳实践:
|
||||
1. 使用 Go Modules 管理依赖
|
||||
2. 遵循包导入的命名规范
|
||||
3. 避免使用点导入和相对路径导入
|
||||
4. 合理使用空白导入
|
||||
5. 定期清理和更新依赖
|
||||
6. 提交 go.mod 和 go.sum 文件
|
||||
7. 使用工具检测循环依赖
|
||||
|
||||
注意事项:
|
||||
1. 包的导入路径必须唯一
|
||||
2. 同一目录下的文件必须属于同一包
|
||||
3. 包名通常与目录名相同
|
||||
4. 避免循环导入
|
||||
5. 合理使用包的可见性规则
|
||||
*/
|
@@ -1,8 +1,48 @@
|
||||
// Package utils 提供一些实用的辅助函数
|
||||
// 这是一个示例包,用于演示包的创建和使用
|
||||
/*
|
||||
Package utils 提供一些实用的辅助函数
|
||||
|
||||
这是一个示例包,用于演示包的创建和使用。
|
||||
包含了数学计算、字符串处理、数据验证等常用功能。
|
||||
|
||||
使用示例:
|
||||
|
||||
import "your-module/08-packages/utils"
|
||||
|
||||
result := utils.Add(1, 2)
|
||||
greeting := utils.GetGreeting("World")
|
||||
*/
|
||||
package utils
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 包级别的常量
|
||||
const (
|
||||
// MaxRetries 最大重试次数
|
||||
MaxRetries = 3
|
||||
|
||||
// DefaultTimeout 默认超时时间
|
||||
DefaultTimeout = 30 * time.Second
|
||||
|
||||
// Version 包版本
|
||||
Version = "1.0.0"
|
||||
)
|
||||
|
||||
// 包级别的变量
|
||||
var (
|
||||
// PackageVersion 包版本(可修改)
|
||||
PackageVersion = "1.0.0"
|
||||
|
||||
// emailRegex 邮箱验证正则表达式
|
||||
emailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
|
||||
)
|
||||
|
||||
// ========== 数学计算函数 ==========
|
||||
|
||||
// Add 计算两个整数的和
|
||||
// 注意:函数名首字母大写,表示这是一个导出的(公开的)函数
|
||||
@@ -10,11 +50,59 @@ func Add(a, b int) int {
|
||||
return a + b
|
||||
}
|
||||
|
||||
// Subtract 计算两个整数的差
|
||||
func Subtract(a, b int) int {
|
||||
return a - b
|
||||
}
|
||||
|
||||
// Multiply 计算两个整数的乘积
|
||||
func Multiply(a, b int) int {
|
||||
return a * b
|
||||
}
|
||||
|
||||
// Divide 计算两个数的商,返回结果和错误
|
||||
func Divide(a, b float64) (float64, error) {
|
||||
if b == 0 {
|
||||
return 0, fmt.Errorf("除数不能为零")
|
||||
}
|
||||
return a / b, nil
|
||||
}
|
||||
|
||||
// Max 返回两个整数中的较大值
|
||||
func Max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// Min 返回两个整数中的较小值
|
||||
func Min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// Abs 返回整数的绝对值
|
||||
func Abs(n int) int {
|
||||
if n < 0 {
|
||||
return -n
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Power 计算 base 的 exp 次方
|
||||
func Power(base, exp int) int {
|
||||
result := 1
|
||||
for i := 0; i < exp; i++ {
|
||||
result *= base
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// ========== 字符串处理函数 ==========
|
||||
|
||||
// greet 是一个私有函数(首字母小写)
|
||||
// 只能在包内部使用,外部无法访问
|
||||
func greet(name string) string {
|
||||
@@ -26,8 +114,206 @@ func GetGreeting(name string) string {
|
||||
return greet(name)
|
||||
}
|
||||
|
||||
// 包级别的变量
|
||||
var PackageVersion = "1.0.0"
|
||||
// Capitalize 将字符串首字母大写
|
||||
func Capitalize(s string) string {
|
||||
if len(s) == 0 {
|
||||
return s
|
||||
}
|
||||
return strings.ToUpper(s[:1]) + strings.ToLower(s[1:])
|
||||
}
|
||||
|
||||
// 包级别的常量
|
||||
const MaxRetries = 3
|
||||
// Reverse 反转字符串
|
||||
func Reverse(s string) string {
|
||||
runes := []rune(s)
|
||||
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
|
||||
runes[i], runes[j] = runes[j], runes[i]
|
||||
}
|
||||
return string(runes)
|
||||
}
|
||||
|
||||
// IsPalindrome 检查字符串是否为回文
|
||||
func IsPalindrome(s string) bool {
|
||||
s = strings.ToLower(strings.ReplaceAll(s, " ", ""))
|
||||
return s == Reverse(s)
|
||||
}
|
||||
|
||||
// WordCount 统计字符串中的单词数量
|
||||
func WordCount(s string) int {
|
||||
words := strings.Fields(s)
|
||||
return len(words)
|
||||
}
|
||||
|
||||
// ========== 数据验证函数 ==========
|
||||
|
||||
// IsEmail 验证邮箱地址格式
|
||||
func IsEmail(email string) bool {
|
||||
return emailRegex.MatchString(email)
|
||||
}
|
||||
|
||||
// IsEmpty 检查字符串是否为空或只包含空白字符
|
||||
func IsEmpty(s string) bool {
|
||||
return strings.TrimSpace(s) == ""
|
||||
}
|
||||
|
||||
// InRange 检查数字是否在指定范围内
|
||||
func InRange(value, min, max int) bool {
|
||||
return value >= min && value <= max
|
||||
}
|
||||
|
||||
// IsPositive 检查数字是否为正数
|
||||
func IsPositive(n int) bool {
|
||||
return n > 0
|
||||
}
|
||||
|
||||
// IsEven 检查数字是否为偶数
|
||||
func IsEven(n int) bool {
|
||||
return n%2 == 0
|
||||
}
|
||||
|
||||
// IsOdd 检查数字是否为奇数
|
||||
func IsOdd(n int) bool {
|
||||
return n%2 != 0
|
||||
}
|
||||
|
||||
// ========== 切片操作函数 ==========
|
||||
|
||||
// Contains 检查切片是否包含指定元素
|
||||
func Contains(slice []int, item int) bool {
|
||||
for _, v := range slice {
|
||||
if v == item {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ContainsString 检查字符串切片是否包含指定字符串
|
||||
func ContainsString(slice []string, item string) bool {
|
||||
for _, v := range slice {
|
||||
if v == item {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RemoveDuplicates 移除整数切片中的重复元素
|
||||
func RemoveDuplicates(slice []int) []int {
|
||||
keys := make(map[int]bool)
|
||||
var result []int
|
||||
|
||||
for _, item := range slice {
|
||||
if !keys[item] {
|
||||
keys[item] = true
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Sum 计算整数切片的总和
|
||||
func Sum(slice []int) int {
|
||||
total := 0
|
||||
for _, v := range slice {
|
||||
total += v
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
// Average 计算整数切片的平均值
|
||||
func Average(slice []int) float64 {
|
||||
if len(slice) == 0 {
|
||||
return 0
|
||||
}
|
||||
return float64(Sum(slice)) / float64(len(slice))
|
||||
}
|
||||
|
||||
// ========== 时间处理函数 ==========
|
||||
|
||||
// FormatDuration 格式化时间间隔
|
||||
func FormatDuration(d time.Duration) string {
|
||||
if d < time.Minute {
|
||||
return fmt.Sprintf("%.1f秒", d.Seconds())
|
||||
} else if d < time.Hour {
|
||||
return fmt.Sprintf("%.1f分钟", d.Minutes())
|
||||
} else if d < 24*time.Hour {
|
||||
return fmt.Sprintf("%.1f小时", d.Hours())
|
||||
} else {
|
||||
return fmt.Sprintf("%.1f天", d.Hours()/24)
|
||||
}
|
||||
}
|
||||
|
||||
// IsWeekend 检查给定日期是否为周末
|
||||
func IsWeekend(t time.Time) bool {
|
||||
weekday := t.Weekday()
|
||||
return weekday == time.Saturday || weekday == time.Sunday
|
||||
}
|
||||
|
||||
// DaysUntil 计算距离指定日期还有多少天
|
||||
func DaysUntil(target time.Time) int {
|
||||
now := time.Now()
|
||||
diff := target.Sub(now)
|
||||
return int(math.Ceil(diff.Hours() / 24))
|
||||
}
|
||||
|
||||
// ========== 类型定义 ==========
|
||||
|
||||
// Point 表示二维坐标点
|
||||
type Point struct {
|
||||
X, Y float64
|
||||
}
|
||||
|
||||
// Distance 计算两点之间的距离
|
||||
func (p Point) Distance(other Point) float64 {
|
||||
dx := p.X - other.X
|
||||
dy := p.Y - other.Y
|
||||
return math.Sqrt(dx*dx + dy*dy)
|
||||
}
|
||||
|
||||
// String 实现 Stringer 接口
|
||||
func (p Point) String() string {
|
||||
return fmt.Sprintf("Point(%.2f, %.2f)", p.X, p.Y)
|
||||
}
|
||||
|
||||
// Rectangle 表示矩形
|
||||
type Rectangle struct {
|
||||
Width, Height float64
|
||||
}
|
||||
|
||||
// Area 计算矩形面积
|
||||
func (r Rectangle) Area() float64 {
|
||||
return r.Width * r.Height
|
||||
}
|
||||
|
||||
// Perimeter 计算矩形周长
|
||||
func (r Rectangle) Perimeter() float64 {
|
||||
return 2 * (r.Width + r.Height)
|
||||
}
|
||||
|
||||
// String 实现 Stringer 接口
|
||||
func (r Rectangle) String() string {
|
||||
return fmt.Sprintf("Rectangle(%.2fx%.2f)", r.Width, r.Height)
|
||||
}
|
||||
|
||||
// ========== 错误类型 ==========
|
||||
|
||||
// ValidationError 表示验证错误
|
||||
type ValidationError struct {
|
||||
Field string
|
||||
Message string
|
||||
}
|
||||
|
||||
// Error 实现 error 接口
|
||||
func (e ValidationError) Error() string {
|
||||
return fmt.Sprintf("验证错误 [%s]: %s", e.Field, e.Message)
|
||||
}
|
||||
|
||||
// ========== 包初始化函数 ==========
|
||||
|
||||
// init 函数在包被导入时自动执行
|
||||
func init() {
|
||||
// 可以在这里进行包的初始化工作
|
||||
// 例如:设置默认值、验证环境等
|
||||
fmt.Printf("utils 包已加载,版本: %s\n", Version)
|
||||
}
|
||||
|
Reference in New Issue
Block a user