ESC
输入关键词搜索文章
目录

Go 入门 04

集合 · 结构体 · 接口 · 测试
从数据建模到行为验证
slice序列
map索引
test验证
Chapter 04
用集合保存数据

Go 入门阶段最常用的集合是 array、slice 和 map。array 是固定长度数组;slice 是更常用的动态序列视图;map 是键值映射。官方 Create a module 教程会用 slice 保存多个问候对象,用 map 保存名字到消息的关系。

定义:slice

slice 是对底层数组某一段的描述,包含长度、容量和指向底层数组的引用。日常 Go 编程里,动态列表通常优先用 slice。

names := []string{"Ada", "Ken", "Rob"}
names = append(names, "Gopher")

for index, name := range names {
    fmt.Println(index, name)
}

定义:map

map 是键值表,通过 key 快速找到 value。读取 map 时可以用第二个返回值判断 key 是否存在。

scores := map[string]int{
    "Ada": 98,
    "Ken": 95,
}

score, ok := scores["Rob"]
if !ok {
    fmt.Println("Rob has no score")
} else {
    fmt.Println(score)
}
Struct
结构体把字段组织成概念
结构体用于把多个字段组合成一个具名概念。它不是“带方法的 map”,而是一个明确的数据模型。字段名、字段类型和方法共同描述这个概念能表达什么。
type User struct {
    ID    int
    Name  string
    Email string
}

func (user User) DisplayName() string {
    if user.Name == "" {
        return "anonymous"
    }
    return user.Name
}
DisplayName 前面的 (user User) 叫接收者,表示这个函数是 User 类型的方法。值接收者适合不修改对象的小方法;指针接收者适合需要修改对象或避免复制较大结构体的场景。
Interface
接口描述行为,而不是继承层级

Go 的 interface 是方法集合。一个类型只要实现了接口要求的所有方法,就自动满足该接口,不需要显式声明 implements。

type Notifier interface {
    Notify(message string) error
}

type EmailNotifier struct {
    Address string
}

func (notifier EmailNotifier) Notify(message string) error {
    fmt.Printf("send %q to %s\n", message, notifier.Address)
    return nil
}

func SendWelcome(notifier Notifier) error {
    return notifier.Notify("welcome")
}

这种设计鼓励你在调用侧定义小接口。调用者只声明自己真正需要的方法,具体类型可以来自当前包、其他包,甚至测试里的假实现。

接口经验:初学者不要一上来就设计大接口。先写具体类型,等出现真实替换需求时,再抽出最小接口。
Testing
用 go test 保护行为

Go 的 testing 包和 go test 命令是标准测试入口。测试文件以 _test.go 结尾,测试函数形如 func TestXxx(*testing.T)。官方 testing 文档说明,go test 会自动执行这些测试函数。

package greeting

import "testing"

func Hello(name string) string {
    if name == "" {
        return "Hello, anonymous"
    }
    return "Hello, " + name
}

func TestHello(t *testing.T) {
    got := Hello("Ada")
    want := "Hello, Ada"

    if got != want {
        t.Errorf("Hello(\"Ada\") = %q, want %q", got, want)
    }
}

执行测试:

go test ./...

测试不是等项目写完才补的文档,而是开发过程中持续校验行为的工具。Go 官方教程也把“添加测试”放在模块入门路径中,说明它属于基础能力,而不是高级技巧。

Generics
泛型先理解用途,不急着滥用

Go 1.18 引入泛型。泛型允许函数或类型在一组类型上复用逻辑,常见场景是容器、算法和类型安全的通用工具。官方泛型教程从两个 map 求和函数开始,展示如何合并成一个泛型函数。

type Number interface {
    ~int64 | ~float64
}

func SumValues[K comparable, V Number](values map[K]V) V {
    var sum V
    for _, value := range values {
        sum += value
    }
    return sum
}

初学阶段先把普通函数、接口和具体类型写熟。只有当你确实在重复同一套类型无关逻辑时,再考虑泛型。

复习速查

  • slice:动态序列,常配合 appendrange
  • map:键值表,读取时用 value, ok 判断是否存在。
  • struct:把字段组织成领域概念。
  • interface:描述一组方法,类型隐式满足接口。
  • testing:测试文件 _test.go,测试函数 TestXxx(*testing.T)

参考来源