Go语言快速入门Go语言快速入门
首页
基础篇
进阶篇
高阶篇
实战篇
Go官方网站
编程指南
首页
基础篇
进阶篇
高阶篇
实战篇
Go官方网站
编程指南
  • 高阶篇

    • 🎯 高阶篇
    • Context
    • 反射
    • 泛型
    • 性能优化
    • 最佳实践

最佳实践

这是Go社区总结的最佳实践,让你的代码更加专业!这些都是我在实际工作中一点一滴积累下来的经验。刚开始写Go的时候,我常常不按这些规范来,后来维护代码的时候苦不堆言。现在把这些分享给你,希望能让你少走些弯路!

代码风格

使用gofmt

# 格式化代码
gofmt -w .

# 或使用
go fmt ./...

命名规范

// 包名:小写,简短,无下划线
package userservice  // ✅
package UserService  // ❌
package user_service // ❌

// 变量名:驼峰命名
userName := "张三"  // ✅
user_name := "张三" // ❌

// 常量:驼峰或全大写
const MaxRetries = 3
const maxRetries = 3
const MAX_RETRIES = 3  // 也可以

// 接口名:动词+er
type Reader interface { Read(p []byte) (n int, err error) }
type Writer interface { Write(p []byte) (n int, err error) }

// 缩写保持一致大写
userID, httpClient, xmlParser  // ✅
userId, httpClient, xmlparser  // ❌(不一致)

函数设计

// 参数数量不超过5个,太多用结构体
// ❌
func CreateUser(name, email, password, phone, address string, age int) {}

// ✅
type CreateUserParams struct {
    Name     string
    Email    string
    Password string
    Phone    string
    Address  string
    Age      int
}
func CreateUser(params CreateUserParams) {}

// 返回值不超过3个
func Parse(s string) (value int, err error) {}  // ✅
func Parse(s string) (value int, remaining string, lineNum int, err error) {}  // ❌

// ✅ 使用结构体
type ParseResult struct {
    Value     int
    Remaining string
    LineNum   int
}
func Parse(s string) (ParseResult, error) {}

错误处理

立即处理错误

// ❌ 错误处理在很远的地方
result, err := doSomething()
// ... 很多代码
if err != nil { }

// ✅ 立即处理
result, err := doSomething()
if err != nil {
    return err
}

添加上下文

// ❌ 直接返回
return err

// ✅ 添加上下文
return fmt.Errorf("读取配置文件失败: %w", err)

// ✅✅ 更多上下文
return fmt.Errorf("初始化服务 %s 失败: %w", serviceName, err)

不要忽略错误

// ❌ 忽略错误
file.Close()
json.Unmarshal(data, &obj)

// ✅ 处理错误
if err := file.Close(); err != nil {
    log.Printf("关闭文件失败: %v", err)
}

// 或使用defer处理
defer func() {
    if err := file.Close(); err != nil {
        log.Printf("关闭文件失败: %v", err)
    }
}()

并发安全

避免数据竞争

// ❌ 数据竞争
var counter int
go func() { counter++ }()
go func() { counter++ }()

// ✅ 使用锁
var (
    counter int
    mu      sync.Mutex
)
go func() { 
    mu.Lock()
    counter++ 
    mu.Unlock()
}()

// ✅ 使用原子操作
var counter int64
go func() { atomic.AddInt64(&counter, 1) }()

// ✅ 使用channel
counter := make(chan int, 1)
counter <- 0
go func() { 
    v := <-counter
    counter <- v + 1
}()

使用race检测

go run -race main.go
go test -race ./...

goroutine生命周期

// ❌ goroutine泄漏
func process() {
    go func() {
        for {
            // 永远运行...
        }
    }()
}

// ✅ 可控制的goroutine
func process(ctx context.Context) {
    go func() {
        for {
            select {
            case <-ctx.Done():
                return  // 优雅退出
            default:
                // 工作
            }
        }
    }()
}

资源管理

使用defer

// ✅ 确保资源释放
func readFile(path string) ([]byte, error) {
    f, err := os.Open(path)
    if err != nil {
        return nil, err
    }
    defer f.Close()  // 确保关闭
    
    return io.ReadAll(f)
}

// 注意defer的参数在声明时求值
func trace(name string) func() {
    start := time.Now()
    fmt.Printf("entering %s\n", name)
    return func() {
        fmt.Printf("leaving %s (%v)\n", name, time.Since(start))
    }
}

func foo() {
    defer trace("foo")()  // 注意这里的()
    // ...
}

连接池

// ✅ 数据库连接池
db, err := sql.Open("mysql", dsn)
if err != nil {
    return err
}
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(25)
db.SetConnMaxLifetime(5 * time.Minute)

// ✅ HTTP客户端复用
var httpClient = &http.Client{
    Timeout: 10 * time.Second,
    Transport: &http.Transport{
        MaxIdleConns:        100,
        MaxIdleConnsPerHost: 10,
        IdleConnTimeout:     90 * time.Second,
    },
}

包设计

避免循环依赖

// ❌ 循环依赖
// package a imports package b
// package b imports package a

// ✅ 使用接口解耦
// package a
type UserRepository interface {
    GetUser(id int) (*User, error)
}

// package b
type UserService struct {
    repo a.UserRepository
}

最小化导出

// ❌ 导出太多
type User struct {
    ID       int
    Name     string
    Password string  // 不应该导出
    salt     string
}

// ✅ 只导出必要的
type User struct {
    ID   int
    Name string
}

type user struct {
    User
    password string
    salt     string
}

接口设计

// ❌ 大接口
type Repository interface {
    Create(item *Item) error
    Update(item *Item) error
    Delete(id int) error
    Get(id int) (*Item, error)
    List() ([]*Item, error)
    Search(query string) ([]*Item, error)
    // ... 更多方法
}

// ✅ 小接口
type Reader interface {
    Get(id int) (*Item, error)
}

type Writer interface {
    Create(item *Item) error
    Update(item *Item) error
}

type Deleter interface {
    Delete(id int) error
}

type ReadWriter interface {
    Reader
    Writer
}

测试

表格驱动测试

func TestAdd(t *testing.T) {
    tests := []struct {
        name     string
        a, b     int
        expected int
    }{
        {"正数", 1, 2, 3},
        {"负数", -1, -2, -3},
        {"零", 0, 0, 0},
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got := Add(tt.a, tt.b)
            if got != tt.expected {
                t.Errorf("Add(%d, %d) = %d; want %d",
                    tt.a, tt.b, got, tt.expected)
            }
        })
    }
}

使用testify

import "github.com/stretchr/testify/assert"

func TestUser(t *testing.T) {
    user := NewUser("张三", 25)
    
    assert.NotNil(t, user)
    assert.Equal(t, "张三", user.Name)
    assert.Equal(t, 25, user.Age)
    assert.True(t, user.IsAdult())
}

Mock接口

// 使用接口便于测试
type UserRepository interface {
    GetByID(id int) (*User, error)
}

// 真实实现
type MySQLUserRepository struct {
    db *sql.DB
}

// Mock实现
type MockUserRepository struct {
    users map[int]*User
}

func (m *MockUserRepository) GetByID(id int) (*User, error) {
    if user, ok := m.users[id]; ok {
        return user, nil
    }
    return nil, errors.New("not found")
}

日志

结构化日志

// ❌ 拼接字符串
log.Printf("用户 %s 登录失败: %v", username, err)

// ✅ 结构化日志
logger.Error("登录失败",
    zap.String("username", username),
    zap.Error(err),
    zap.Time("timestamp", time.Now()),
)

日志级别

// 使用适当的日志级别
logger.Debug("详细调试信息")
logger.Info("一般信息")
logger.Warn("警告信息")
logger.Error("错误信息")
logger.Fatal("致命错误,程序退出")

配置管理

使用环境变量

// 从环境变量读取配置
port := os.Getenv("PORT")
if port == "" {
    port = "8080"
}

// 使用库简化
import "github.com/kelseyhightower/envconfig"

type Config struct {
    Port     int    `envconfig:"PORT" default:"8080"`
    Database string `envconfig:"DATABASE_URL" required:"true"`
    Debug    bool   `envconfig:"DEBUG" default:"false"`
}

var cfg Config
envconfig.Process("", &cfg)

配置文件

// config.yaml
type Config struct {
    Server struct {
        Port int    `yaml:"port"`
        Host string `yaml:"host"`
    } `yaml:"server"`
    Database struct {
        DSN string `yaml:"dsn"`
    } `yaml:"database"`
}

func LoadConfig(path string) (*Config, error) {
    data, err := os.ReadFile(path)
    if err != nil {
        return nil, err
    }
    
    var cfg Config
    if err := yaml.Unmarshal(data, &cfg); err != nil {
        return nil, err
    }
    return &cfg, nil
}

项目结构

myproject/
├── cmd/                    # 可执行程序
│   └── myapp/
│       └── main.go
├── internal/               # 私有代码
│   ├── config/
│   ├── handler/
│   ├── service/
│   └── repository/
├── pkg/                    # 公开库
│   └── utils/
├── api/                    # API定义
│   └── proto/
├── web/                    # 静态文件
├── scripts/                # 脚本
├── deployments/            # 部署配置
├── docs/                   # 文档
├── go.mod
├── go.sum
├── Makefile
└── README.md

代码审查清单

功能性

  • [ ] 代码实现了需求
  • [ ] 边界条件处理正确
  • [ ] 错误处理完善

可读性

  • [ ] 命名清晰有意义
  • [ ] 代码结构清晰
  • [ ] 有必要的注释

安全性

  • [ ] 没有SQL注入
  • [ ] 没有敏感信息硬编码
  • [ ] 输入有验证

性能

  • [ ] 没有明显的性能问题
  • [ ] 资源正确释放
  • [ ] 没有goroutine泄漏

测试

  • [ ] 有单元测试
  • [ ] 测试覆盖率足够
  • [ ] 测试可读性好

推荐工具

开发工具

# 格式化
gofmt, goimports

# 静态分析
go vet
golangci-lint

# 依赖管理
go mod

# 测试
go test

CI/CD配置

# .github/workflows/go.yml
name: Go
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - uses: actions/setup-go@v4
      with:
        go-version: '1.21'
    - run: go build ./...
    - run: go test ./...
    - run: go vet ./...

总结

Go之禅

简单胜于复杂
明确胜于隐晦
少即是多
可读性很重要
错误要处理
并发要安全
接口要小
测试要充分

学习资源

  • Go官方文档
  • Effective Go
  • Go Code Review Comments
  • Go Proverbs

🎉 恭喜你完成了整个Go语言教程!

你已经从基础到高阶系统地学习了Go语言。现在:

  1. 动手实践 - 用Go写一个自己的项目
  2. 阅读源码 - 看看优秀的开源项目
  3. 持续学习 - Go社区在不断发展

祝你Go程愉快! 🚀

最近更新: 2025/12/27 13:26
Contributors: 王长安
Prev
性能优化