20-包及包管理

0 阅读4分钟

包 == package 为什么要用包?

  • 更好的管理项目模块、功能
  • 实现同名方法、变量等
  • 控制访问范围(又称作用域)
  • 总结: 就是更好的分功能、分模块的管理代码

Go Module依赖管理

go mod init [模块名称]

  • 同一个目录下的代码 package 声明一样不需要引入
  • 引入第三方包 go get 包地址
  • 没有使用的可以用 _ 重命名
    • 也会执行对应包里面的init函数
    • 包的init函数可以是多个
  • 引入包会默认的依次执行 init 函数
  • 目录结构
// => 项目 g6
|- main.go
|- util
    | - a.go (package utils)
    | - b.go (package utils)
    | - math
        | - math.go (package maths)
  • main.go
  1. go get github.com/bytedance/sonic
package main
import (
    "fmt"
    "g6/util"
    math "g6/util/main" //别名
    "bytedance/sonic"
)

func main() {
    fmt.Println(util.Name)
    fmt.Println(util.Add(1, 2, 3))
    fmt.Println(math.Add(1, 2))//没有别名的化 maths.Add()
}

go mod tidy

  • 当发现代码里面引入了第三方包的时候,自动下载引入第三方包(go.mod)

包函数的使用

  • 非本package(包)中的函数
    • 导入使用的包
      • import ""
      • package name = + 目录
    • 调包里面的函数
      • <函数名称> -> 直接用就完事了
package main

import (
    "fmt"
    utils "ls/utils"//起别名
)

func main() {}

包引入

  • github.com/spf13/cobra
  • go mod tidy拉包
package main

import (
	"fmt"
	// "os"
	"github.com/spf13/cobra"
	// _ "github.com/spf13/cobra" //用来转类型的
	// go mod tidy
)

// 让我们的体脂计算器接收传入的姓名、性别、身高、体重来计算体脂率

func main() {
	var (
		name   string
		gender string
		// tail   string // float64
		// weight string //float64
		// age    string //int
		tail   float64
		weight float64
		age    int
	)

	// arguments := os.Args
	// name = arguments[1]
	// gender = arguments[2]
	// tail = arguments[3]
	// weight = arguments[4]
	// age = arguments[5]

	cmd := &cobra.Command{
		Use:   "health",                //命令行的名字
		Short: "体脂计算器、根据身高、体重、性别、年龄计算", //短描述
		Long:  "该体脂率计算基于BMI的体脂计算...",   //长描述
		Run: func(cmd *cobra.Command, args []string) {
			// 计算
			// bmi := calculator.CalcBMI(tail, weight)
			// fatRate := calculator.CalcFatRate(bmi, age, gender)

			// 评估结果
			// 别的包区实现
		},
	}
	cmd.Flags().StringVar(&name, "name", "", "姓名")
	cmd.Flags().StringVar(&gender, "gender", "", "性别")
	cmd.Flags().Float64Var(&tail, "tail", 0, "身高")
	cmd.Flags().Float64Var(&weight, "weight", 0, "体重")
	cmd.Flags().IntVar(&age, "age", 0, "年龄")

	fmt.Println(name, gender, tail, weight, age)
	// go run . 小白 男 1.7 60 29

	cmd.ExecuteC()
}

/*
go run main.go -help
  health [flags]

Flags:
      --age int         年龄
      --gender string   性别
  -h, --help            help for health
      --name string     姓名
      --tail float      身高
      --weight float    体重

自定义顺序
go run main.go --age 18 --name 江小北 --weight 65 --tail 1.7 --gender 男
*/
  • go.mod: 做包管理的的文件
  • go.sum: 隐藏的依赖

go module进阶

  • 编写代码时,会遇到找不到 package、func的错误,运行
    • go get -> 指定的包
    • go mod tidy
  • 像正常代码使用
  • go.mod 文件务必在 打开文件夹的位置
  • 运行 go mod tidy 来保证项目中使用到的 go modules被包含在项目依赖中

Go Module 替换

  • Go Module 替换(replace) 是用另一个实现替换默认要使用的实现
  1. 默认 tidy 出来的版本是最新版本,有时候是不合适的,则需要替换为相对旧的版本
  2. 我们提供定制的版本,并开源在git服务上,需要将 module重定向
  3. 本地有些项目的代码,需要作为独立的 module 使用
  • go.mod
replace (
    //包
    地址 版本号(v0.0.0)
)

思考

  • Go Module 管理依赖已经很完善了,是不是使用了Go Module 就万无一失了?
  1. 不是万无一失的 1.1 Go Module 完全依赖git服务,与git上的branch、release tag 相关。 1.2 Git 服务不属于 Go Module 的管理范围 1.3 Git 上的branch、release 可以随时被删除、重写
  2. 对项目的影响 2.1 项目可能因为 git 服务的变更无法编译 2.2 编译出来的程序行为不一致

Vendor 简介

  • vendor 是把项目 module 定义的所有依赖制作一个副本保存在项目的vendor 目录
    • 就是将项目定义的依赖做一个快照并保存下来,避免项目依赖的变更影响项目的一致性
  • Go module 深度支持 vendor, 通过命令 go mod vendor 将项目依赖保存在 vendor中
    • 会产生一个 vendor 文件夹

Go module 与 vendor共存

  • Go build/run 通过命令行选项来控制 -mod=<vendor|mod|readonly>
    • vendor: 使用vendor中的依赖来编译项目
    • mod: 使用go module 定义的依赖编译项目, 并且会自动更新 go.mod 定义
    • readonly: 使用 go module定义的依赖编译项目,并且不做任何依赖的升级
  • 注意
    • 项目中如果有vendor目录,Go在编译时默认使用vendor提供的依赖
    • 如果没有 vendor目录,Go在编译时默认使用readonly

目录

  • 整洁代码的价值
    • 更容易看懂
    • 更容易修改
    • 更容易测试
    • 更容易上线
  • 方法、函数内部
    • 通常100 行左右的方法会比较容易理解
    • 怎么避免冗余代码
      • 将代码拆分为多个子方法
      • 提出特定的对象,将各个方法转换为特定对象的方法
    • 主逻辑清晰
    • 复杂逻辑的嵌套: 多个 if-else、for、switch的控制代码
      • 嵌套内部的逻辑拆分为子方法
    • 目的单一
      • 方法的目的要单一: 一次只做一件事情。事情可大可小
  • 方法、函数之间
    • 起一个好名字: 见名知意
    • 避免太多的方法参数、返回值
    • 一致的方法抽象
    • 读写分离