跳转到内容

Go语言模式

来源: everything-claude-code 技能库

golang-patterns 涵盖 Go 语言的惯用语模式、最佳实践和约定,用于构建健壮、高效和可维护的应用。

Go 偏向简洁而非巧妙。代码应该显而易见、易于阅读:

// 好:清晰直接
func GetUser(id string) (*User, error) {
user, err := db.FindUser(id)
if err != nil {
return nil, fmt.Errorf("get user %s: %w", id, err)
}
return user, nil
}
// 不好:过度巧妙
func GetUser(id string) (*User, error) {
return func() (*User, error) {
if u, e := db.FindUser(id); e == nil {
return u, nil
} else {
return nil, e
}
}()
}

设计类型时,让零值无需初始化即可使用:

// 好:零值就有用
var mu sync.Mutex // 无需初始化即可使用
var ch chan int // 无需 make 即可使用
var m map[string]int // 零值是 nil,但读取安全
// 不好:需要显式初始化
mu := &sync.Mutex{} // 不必要

Go 没有继承,使用组合代替:

// 好:组合
type Reader struct {
io.Reader
logger Logger
}
// 不好:模拟继承
type Reader struct {
io.Reader
// 这不是继承,只是嵌入
}

Go 没有异常,错误是返回值:

// 好:显式错误处理
func ReadFile(path string) ([]byte, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("read file %s: %w", path, err)
}
return data, nil
}
// 不好:忽略错误
data, _ := os.ReadFile(path) // 错误被忽略!
func ReadConfig(path string) (*Config, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("reading config: %w", err)
}
var cfg Config
if err := json.Unmarshal(data, &cfg); err != nil {
return nil, fmt.Errorf("parsing config: %w", err)
}
return &cfg, nil
}
func FetchUser(ctx context.Context, id string) (*User, error) {
req, err := http.NewRequestWithContext(ctx, "GET", "/users/"+id, nil)
if err != nil {
return nil, err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
// ...
}
func fetchAll(urls []string) []Result {
results := make([]Result, len(urls))
var wg sync.WaitGroup
for i, url := range urls {
wg.Add(1)
go func(i int, url string) {
defer wg.Done()
results[i] = fetch(url)
}(i, url)
}
wg.Wait()
return results
}
type Cache struct {
mu sync.RWMutex
items map[string]interface{}
}
func (c *Cache) Get(key string) (interface{}, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
val, ok := c.items[key]
return val, ok
}
func (c *Cache) Set(key string, value interface{}) {
c.mu.Lock()
defer c.mu.Unlock()
c.items[key] = value
}
type QueryBuilder struct {
query strings.Builder
params []interface{}
}
func NewQueryBuilder() *QueryBuilder {
return &QueryBuilder{
params: make([]interface{}, 0),
}
}
func (qb *QueryBuilder) Select(fields ...string) *QueryBuilder {
qb.query.WriteString("SELECT ")
qb.query.WriteString(strings.Join(fields, ", "))
return qb
}
func (qb *QueryBuilder) From(table string) *QueryBuilder {
qb.query.WriteString(" FROM ")
qb.query.WriteString(table)
return qb
}
func (qb *QueryBuilder) Build() (string, []interface{}) {
return qb.query.String(), qb.params
}
type Server struct {
addr string
port int
maxConns int
readTimeout time.Duration
writeTimeout time.Duration
}
type Option func(*Server)
func Addr(addr string) Option {
return func(s *Server) {
s.addr = addr
}
}
func Port(port int) Option {
return func(s *Server) {
s.port = port
}
}
func NewServer(opts ...Option) *Server {
s := &Server{
addr: "localhost",
port: 8080,
}
for _, opt := range opts {
opt(s)
}
return s
}
// 使用
server := NewServer(Addr("0.0.0.0"), Port(9090))
✅ 正确做法:
1. 返回错误而非忽略
2. 使用 %w 包装错误
3. 提供有意义的错误信息
4. 适当处理上游错误
❌ 错误做法:
1. 使用 _ 忽略错误
2. 返回裸错误
3. 错误信息不清晰
✅ 正确做法:
1. 使用 sync.Mutex 保护共享数据
2. 使用 channel 进行通信
3. 使用 context 取消操作
4. 注意 race condition
❌ 错误做法:
1. 全局变量不加锁
2. channel 和 mutex 混用
3. 不检查 context 取消
✅ 正确做法:
1. 小接口,专注单一职责
2. 依赖接口而非具体实现
3. 接口放在需要的地方
4. 避免不必要的接口
❌ 错误做法:
1. 大而全的接口
2. 过度抽象
3. 接口定义过早
Terminal window
# 格式化代码
go fmt .
gofmt -w .
# 代码检查
go vet ./...
golint ./...
staticcheck ./...
# 依赖管理
go mod tidy
go mod download
# 测试
go test -v ./...
go test -race ./...
go test -coverprofile=coverage.out
# 构建
go build -o app
go build -ldflags="-s -w" app
# 文档
go doc fmt
go doc -all

忽略错误

错误:使用 _ 忽略错误 正确:处理或记录每个错误

全局变量

错误:使用全局共享变量 正确:使用依赖注入

过度并发

错误:创建过多 goroutine 正确:使用 worker pool 或 semaphore

不使用 context

错误:不传递 context 正确:context 用于取消和超时

何时使用

  • 编写新 Go 代码
  • 审查 Go 代码
  • 重构现有代码
  • 设计 Go 包

何时不用

  • 简单脚本
  • 学习实验
  • 性能关键路径(需 benchmark)

关键要点

  • 简洁优先
  • 零值有用
  • 显式错误处理
  • 组合优于继承

常见错误

  • 忽略错误
  • 全局变量
  • goroutine 泄漏
  • 不使用 context
  1. 简单 - 少即是多
  2. 显式 - 避免魔法
  3. 组合 - 优于继承
  4. 并发 - 天然支持
  • 小接口 - io.Reader, io.Writer 只有单一方法
  • 依赖倒置 - 客户端定义接口
  • duck typing - 结构化类型系统
  • 不要通过共享内存通信,通过通信共享内存
  • 使用 channel 协调 goroutine
  • 使用 context 处理取消
  • 注意资源清理
技能关系
golang-testingGo 测试
code-review代码审查
tdd-workflowTDD 工作流
  1. 初始化模块 - go mod init
  2. 编写代码 - 遵循 Go 惯例
  3. 运行测试 - go test -race ./...
  4. 代码检查 - go vet ./...
  5. 格式化 - go fmt ./

官方原文: golang-patterns SKILL.md


💡 提示:Go 的哲学是「少即是多」,写更少的代码,做更多的事。