Skip to content

📐 最佳实践

基于 lzt 项目实际开发经验总结的 Go 语言编程最佳实践,涵盖代码规范、架构设计、性能优化等核心领域。

🎯 实践原则

核心理念

  • 可读性优先 - 代码是写给人看的,机器只是恰好能执行
  • 简单胜于复杂 - 遵循 Go 的简洁哲学
  • 测试驱动开发 - TDD 确保代码质量和设计合理性
  • 持续重构 - 代码质量是迭代改进的结果

实践体系

📋 Go 编程规范

命名规范

包命名

go
// ✅ 好的包名
package bubble    // 简洁、描述性
package ledger    // 表达业务领域
package errors    // 通用工具包

// ❌ 避免的包名
package bubbleteaui     // 过长
package utils           // 过于通用
package helpers         // 意义不明确

变量和函数命名

go
// ✅ 清晰的命名
func CreateTransaction(req *CreateTransactionRequest) (*Transaction, error)
func ProcessFiles(paths []string, processor func(string) error) error
var maxWorkerCount = 10

// ❌ 不清晰的命名
func Create(r *Req) (*T, error)
func Process(p []string, f func(string) error) error
var max = 10

错误处理

标准错误处理模式

go
// ✅ 推荐的错误处理
func ProcessTransaction(tx *Transaction) error {
    if err := validateTransaction(tx); err != nil {
        return fmt.Errorf("validation failed: %w", err)
    }
    
    if err := saveTransaction(tx); err != nil {
        return fmt.Errorf("save failed: %w", err)
    }
    
    return nil
}

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

func (e *ValidationError) Error() string {
    return fmt.Sprintf("validation error on %s: %s", e.Field, e.Message)
}

接口设计

小而专注的接口

go
// ✅ 单一职责接口
type TaskProcessor interface {
    Process(task Task) error
}

type TaskProvider interface {
    Next() (Task, bool)
}

// ❌ 过大的接口
type TaskManager interface {
    Process(task Task) error
    Next() (Task, bool)
    Validate(task Task) error
    Save(task Task) error
    Delete(id string) error
}

🏗️ 架构设计模式

分层架构

项目中的分层结构

internal/app/ledger/
├── application/          # 应用层
│   └── services/        # 应用服务
├── domain/              # 领域层
│   ├── entities/        # 实体
│   └── repositories/    # 仓储接口
└── infrastructure/      # 基础设施层(在 internal/pkg/)

依赖注入模式

go
// ✅ 使用 Wire 进行依赖注入
//go:build wireinject

package main

import (
    "github.com/google/wire"
    "lzt/internal/app/ledger"
    "lzt/internal/pkg/database"
)

func InitializeLedgerService() (*ledger.Service, error) {
    wire.Build(
        database.NewDB,
        ledger.NewRepository,
        ledger.NewService,
    )
    return nil, nil
}

设计模式应用

仓储模式

go
// 定义仓储接口
type TransactionRepository interface {
    Create(ctx context.Context, tx *Transaction) error
    GetByID(ctx context.Context, id string) (*Transaction, error)
    List(ctx context.Context, filter Filter) ([]*Transaction, error)
}

// 具体实现
type gormTransactionRepository struct {
    db *gorm.DB
}

func (r *gormTransactionRepository) Create(ctx context.Context, tx *Transaction) error {
    return r.db.WithContext(ctx).Create(tx).Error
}

策略模式

go
// 处理策略接口
type ProgressDisplayStrategy interface {
    Display(progress Progress) string
}

// 不同策略实现
type ViewportStrategy struct{}
func (s *ViewportStrategy) Display(progress Progress) string { /* ... */ }

type FullOutputStrategy struct{}
func (s *FullOutputStrategy) Display(progress Progress) string { /* ... */ }

📊 代码质量标准

质量指标

指标要求说明
单元测试覆盖率≥ 80%pkg/ 包必须达到
包级别测试100%所有 pkg/ 包必须有测试
循环复杂度≤ 10单个函数的复杂度
函数长度≤ 50 行超过需要重构
文件长度≤ 500 行避免过长文件

代码审查清单

功能性检查

  • [ ] 功能是否符合需求
  • [ ] 边界条件是否处理
  • [ ] 错误处理是否完整
  • [ ] 性能是否符合要求

代码质量检查

  • [ ] 命名是否清晰易懂
  • [ ] 函数是否单一职责
  • [ ] 是否有适当的注释
  • [ ] 是否遵循项目规范

测试检查

  • [ ] 是否有相应的单元测试
  • [ ] 测试覆盖率是否达标
  • [ ] 测试用例是否充分
  • [ ] 是否有集成测试

🔄 重构案例研究

案例1:View 函数重构

重构前的问题

go
// ❌ 违反单一职责原则
func (m Model) View() string {
    var b strings.Builder
    
    // 渲染进度条
    if m.showProgress {
        b.WriteString(m.renderProgress())
    }
    
    // 渲染任务列表
    if m.showTasks {
        b.WriteString(m.renderTasks())
    }
    
    // 渲染错误信息
    if len(m.errors) > 0 {
        b.WriteString(m.renderErrors())
    }
    
    // 渲染统计信息
    if m.showStats {
        b.WriteString(m.renderStats())
    }
    
    return b.String()
}

重构后的解决方案

go
// ✅ 职责分离,可测试
func (m Model) View() string {
    sections := m.collectViewSections()
    return m.renderSections(sections)
}

func (m Model) collectViewSections() []ViewSection {
    var sections []ViewSection
    
    if m.showProgress {
        sections = append(sections, m.createProgressSection())
    }
    
    if m.showTasks {
        sections = append(sections, m.createTasksSection())
    }
    
    return sections
}

type ViewSection struct {
    Type    SectionType
    Content string
    Style   lipgloss.Style
}

案例2:Context 传递改进

重构前的问题

go
// ❌ 全局状态,难以测试
var globalConfig *Config

func ProcessTask(task Task) error {
    // 依赖全局状态
    if globalConfig.Timeout > 0 {
        // 处理超时逻辑
    }
    return nil
}

重构后的解决方案

go
// ✅ 依赖注入,便于测试
func ProcessTask(ctx context.Context, task Task, config *Config) error {
    select {
    case <-time.After(config.Timeout):
        return context.DeadlineExceeded
    case <-ctx.Done():
        return ctx.Err()
    default:
        // 处理任务
        return nil
    }
}

📚 学习资源

项目实践案例

相关文档

外部参考


💡 实践建议: 编程最佳实践不是一成不变的规则,而是在实际项目中不断验证和改进的经验总结。建议结合具体业务场景灵活应用。

基于 MIT 许可证发布