📐 最佳实践
基于 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
}
}📚 学习资源
项目实践案例
相关文档
外部参考
💡 实践建议: 编程最佳实践不是一成不变的规则,而是在实际项目中不断验证和改进的经验总结。建议结合具体业务场景灵活应用。