Go 语言规范

45.1 命名规范

包名

  • 全部小写
  • 简短、有意义
  • 不使用下划线或驼峰
// 好的命名
package http
package json
package user

// 不好的命名
package httpClient
package user_service

变量名

  • 驼峰命名法
  • 首字母大小写决定可见性
// 公开变量
var MaxSize int
var DefaultTimeout = 30 * time.Second

// 私有变量
var maxSize int
var defaultTimeout = 30 * time.Second

// 简短命名(局部变量)
for i := 0; i < 10; i++ { }
for _, v := range slice { }

函数名

  • 驼峰命名法
  • 动词开头
// 公开函数
func GetUser(id int) (*User, error)
func CalculateTotal(items []Item) int
func NewUserService() *UserService

// 私有函数
func getUserByID(id int) (*User, error)
func calculateTotal(items []Item) int

接口名

  • 单方法接口以 "er" 结尾
  • 描述行为
type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

type Closer interface {
    Close() error
}

常量名

// 公开常量
const (
    StatusOK    = 200
    StatusError = 500
)

// 私有常量
const (
    defaultPort    = 8080
    maxRetryCount  = 3
)

45.2 代码格式

使用 gofmt

# 格式化代码
gofmt -w main.go

# 格式化整个项目
gofmt -w .

缩进和空格

// 使用 Tab 缩进
func main() {
    if true {
        fmt.Println("Hello")
    }
}

// 运算符两侧加空格
result := a + b
if x > 0 {
    // ...
}

// 逗号后加空格
func add(a, b int) int {
    return a + b
}

导入排序

import (
    // 标准库
    "fmt"
    "os"

    // 第三方库
    "github.com/gin-gonic/gin"
    "gorm.io/gorm"

    // 本地包
    "myproject/internal/user"
    "myproject/pkg/utils"
)

45.3 注释规范

包注释

// Package user 提供用户管理功能。
//
// 该包包含用户的 CRUD 操作、认证和授权功能。
// 使用 NewService 创建服务实例。
//
// 示例:
//
//  service := user.NewService(db)
//  user, err := service.GetByID(1)
package user

函数注释

// GetByID 根据用户 ID 获取用户信息。
//
// 参数:
//   - id: 用户 ID
//
// 返回:
//   - *User: 用户信息
//   - error: 错误信息,用户不存在时返回 ErrNotFound
//
// 示例:
//
//  user, err := service.GetByID(1)
//  if err != nil {
//      log.Fatal(err)
//  }
func (s *Service) GetByID(id int) (*User, error) {
    // ...
}

类型注释

// User 表示系统用户。
type User struct {
    ID       int    // 用户 ID
    Username string // 用户名
    Email    string // 邮箱地址
    Status   int    // 状态:1-正常,0-禁用
}

TODO 注释

// TODO(username): 添加缓存支持
// FIXME(username): 修复并发问题
// NOTE: 这里使用了特殊算法

45.4 错误处理规范

错误类型

// 哨兵错误
var ErrNotFound = errors.New("记录不存在")

// 自定义错误类型
type ValidationError struct {
    Field   string
    Message string
}

func (e *ValidationError) Error() string {
    return fmt.Sprintf("验证错误 [%s]: %s", e.Field, e.Message)
}

错误处理模式

// 好的做法:及早返回
func Process(data string) error {
    if data == "" {
        return errors.New("数据不能为空")
    }

    result, err := parse(data)
    if err != nil {
        return fmt.Errorf("解析失败: %w", err)
    }

    return save(result)
}

// 不好的做法:嵌套太深
func ProcessBad(data string) error {
    if data != "" {
        result, err := parse(data)
        if err == nil {
            err = save(result)
            if err == nil {
                return nil
            } else {
                return err
            }
        } else {
            return err
        }
    } else {
        return errors.New("数据不能为空")
    }
}

45.5 项目结构规范

标准项目结构

myproject/
├── cmd/                    # 主应用程序
│   ├── api/
│   │   └── main.go
│   └── cli/
│       └── main.go
├── internal/               # 私有代码
│   ├── handler/
│   ├── service/
│   ├── repository/
│   └── model/
├── pkg/                    # 公开代码
│   └── utils/
├── api/                    # API 定义
│   └── proto/
├── configs/                # 配置文件
├── docs/                   # 文档
├── scripts/                # 脚本
├── test/                   # 测试
├── go.mod
├── go.sum
├── Makefile
└── README.md

库项目结构

mylib/
├── cache.go
├── cache_test.go
├── options.go
├── internal/
│   └── store/
├── examples/
├── go.mod
├── go.sum
├── LICENSE
└── README.md

45.6 接口设计规范

接口隔离

// 好的做法:小接口
type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

// 不好的做法:大接口
type ReadWriter interface {
    Read(p []byte) (n int, err error)
    Write(p []byte) (n int, err error)
    Close() error
    Flush() error
}

接口命名

// 单方法接口以 "er" 结尾
type Reader interface { Read(...) }
type Writer interface { Write(...) }
type Closer interface { Close(...) }

// 组合接口
type ReadWriter interface {
    Reader
    Writer
}

45.7 并发规范

Goroutine 启动

// 好的做法:传递 Context
func Process(ctx context.Context, data string) {
    go func() {
        select {
        case <-ctx.Done():
            return
        default:
            // 处理逻辑
        }
    }()
}

// 不好的做法:无法控制
func ProcessBad(data string) {
    go func() {
        // 无法停止
    }()
}

Channel 使用

// 好的做法:发送方关闭
func producer(ch chan<- int) {
    defer close(ch)
    for i := 0; i < 10; i++ {
        ch <- i
    }
}

// 接收方检查关闭
func consumer(ch <-chan int) {
    for v := range ch {
        fmt.Println(v)
    }
}

45.8 测试规范

测试文件

// user_test.go
package user

import "testing"

func TestGetByID(t *testing.T) {
    tests := []struct {
        name    string
        id      int
        want    *User
        wantErr bool
    }{
        {"正常", 1, &User{ID: 1}, false},
        {"不存在", 999, nil, true},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got, err := GetByID(tt.id)
            if (err != nil) != tt.wantErr {
                t.Errorf("GetByID() error = %v, wantErr %v", err, tt.wantErr)
            }
            // ...
        })
    }
}

基准测试

func BenchmarkGetByID(b *testing.B) {
    for i := 0; i < b.N; i++ {
        GetByID(1)
    }
}

45.9 代码审查清单

提交前检查

  • 代码通过 gofmt 格式化
  • 代码通过 go vet 检查
  • 所有测试通过
  • 添加必要的注释
  • 错误处理完整
  • 没有硬编码的敏感信息
  • 依赖已更新到 go.mod

使用工具

# 格式化
gofmt -w .

# 静态检查
go vet ./...

# 测试
go test ./...

# 竞态检测
go test -race ./...

# 代码检查
golangci-lint run

45.10 小结

本章介绍了 Go 语言的编码规范:

  1. 命名规范:包名、变量名、函数名、接口名
  2. 代码格式:使用 gofmt 格式化
  3. 注释规范:包注释、函数注释、类型注释
  4. 错误处理:及早返回、错误包装
  5. 项目结构:标准项目结构和库项目结构
  6. 接口设计:小接口、接口隔离
  7. 并发规范:Context 传递、Channel 使用
  8. 测试规范:表格驱动测试、基准测试

遵循规范能让你写出更专业、更易维护的 Go 代码。在下一章中,我们将学习常用标准库。