SuperAPI AI 工具集

golang-patterns

编程与开发

查看项目

更新于 10-15 10:36

技能介绍

golang-patterns 是一套面向 Go 语言的惯用模式与最佳实践集合,用于构建健壮、高效、可维护的 Go 应用。它适用于编写新的 Go 代码、审查 Go 代码、重构已有 Go 代码,以及设计 Go 包或模块。

核心原则

Go 偏好简洁而非炫技,代码应当直观易读。示例中对比了清晰直接的 GetUser 实现与过度嵌套的写法。类型设计应让零值即可直接使用,例如带 sync.Mutex 的 Counter 结构体,其 count 零值为 0 可直接使用;bytes.Buffer 的零值同样可用;而含 nil map 的类型则会在使用时 panic。函数应接受接口参数并返回具体类型,例如 ProcessData(r io.Reader) (*Result, error),而不是返回接口从而不必要地隐藏实现细节。

错误处理模式

使用 fmt.Errorf 配合 %w 包装错误并附加上下文,例如加载配置时分别包装读取与解析错误。可定义领域相关的自定义错误类型(如 ValidationError)以及用于常见情况的哨兵错误(ErrNotFound、ErrUnauthorized、ErrInvalidInput)。使用 errors.Is 检查特定错误、errors.As 检查错误类型。不应忽略错误:避免用空白标识符丢弃错误;仅在错误确实无关紧要时(少见)才可忽略,并应说明原因。

并发模式

工作池模式通过 sync.WaitGroup 启动固定数量 worker 消费 jobs 通道并写入 results。使用 context 实现取消与超时,例如 FetchWithTimeout 通过 context.WithTimeout 设置 5 秒超时并用 http.NewRequestWithContext 发起请求。优雅关闭通过 signal.Notify 监听 SIGINT/SIGTERM,再用 server.Shutdown 在 30 秒超时内关闭。errgroup 用于协调多个 goroutine,例如 FetchAll 并发抓取多个 URL 并在任一失败时返回错误。避免 goroutine 泄漏:使用带缓冲通道并配合 select 监听 ctx.Done(),防止发送方永久阻塞。

接口设计

接口应小而专注,如单方法的 Reader、Writer、Closer,并按需组合成 ReadWriteCloser。接口应在使用方(消费者包)定义,而非提供方,例如 service 包定义自己需要的 UserStore 接口。可选行为可通过类型断言实现,例如写入后若 writer 实现了 Flusher 则调用 Flush。

包组织

标准项目布局包含 cmd/(入口)、internal/(handler、service、repository、config)、pkg/(公开 API 客户端)、api/(proto、OpenAPI 定义)、testdata/ 以及 go.mod、go.sum、Makefile。包命名应简短、小写、无下划线,避免 httpHandler、json_parser 这类写法,也避免冗余的 userService 后缀。应避免包级可变状态,改用依赖注入,例如通过 NewServer(db *sql.DB) 传入依赖。

结构体设计

函数式选项模式通过 Option 函数类型与 WithTimeout、WithLogger 等选项函数,在 NewServer 中先设默认值再依次应用选项。嵌入用于组合,例如 Server 嵌入 *Logger 后可直接调用 Log 方法。

内存与性能

已知大小时应预分配切片,使用 make([]Result, 0, len(items)) 而非反复 append 扩容。频繁分配可使用 sync.Pool,例如复用 bytes.Buffer 并在归还前 Reset。避免在循环中拼接字符串,应使用 strings.Builder,或直接使用标准库 strings.Join。

Go 工具链集成

常用命令包括:go build ./...、go run ./cmd/myapp;go test ./...、go test -race ./...、go test -cover ./...;go vet ./...、staticcheck ./...、golangci-lint run;go mod tidy、go mod verify;gofmt -w .、goimports -w .。推荐的 .golangci.yml 启用 errcheck、gosimple、govet、ineffassign、staticcheck、unused、gofmt、goimports、misspell、unconvert、unparam 等 linter,并配置 errcheck 检查类型断言、govet 启用 shadow。

惯用法速查

接受接口、返回结构体;错误是值,应作为一等公民而非异常;不要通过共享内存来通信,而应使用通道协调 goroutine;让零值可用;少量复制优于少量依赖;清晰优于聪明;始终用 gofmt/goimports 格式化;尽早返回,先处理错误,保持正常路径不缩进。

应避免的反模式

长函数中使用裸返回;用 panic 控制流程;把 context 放进结构体(context 应作为第一个参数);混用值接收者与指针接收者(应保持一致)。

总体而言,Go 代码应当以最好的方式保持“无聊”——可预测、一致、易于理解;拿不准时就保持简单。