mcpField
G
go-design-patterns

go-design-patterns

github.com/eduardo-sl开发83152 次安装2 周前更新
代码仓库

关于 go-design-patterns

面向 Go 开发的编码技能,指导开发者采用惯用的构造、行为、弹性与类型设计模式,并提供按需查阅的详细参考资料。

主要功能

  1. 01按设计需求选择模式
  2. 02函数选项与构造函数
  3. 03工厂与中间件组合
  4. 04重试、熔断器与隔离舱
  5. 05用类型表达有效状态

适用场景

  1. 01为 Go API 选择配置模式
  2. 02为依赖调用增加弹性
  3. 03组织中间件与装饰器
  4. 04表示经过验证的值和生命周期

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/eduardo-sl/go-agent-skills --skill go-design-patterns

安装后在 Claude Code 中输入/go-design-patterns确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
behavioral-patterns.md
creation-patterns.md
resilience-and-types.md

详细介绍

Go 设计模式

Go 倾向于组合而非继承,倾向于简单而非抽象。 这些模式符合 Go 惯用法,不是移植到 Go 的 Java 模式。

社区默认规则。如果项目技能或 Agent 配置规则明确取代了 go-design-patterns,则以该规则为准。

详细参考资料,按需加载:

  • references/creation-patterns.md — 函数选项(完整示例)、 选项与配置结构体的比较、构造函数、工厂。
  • references/behavioral-patterns.md — 策略、中间件/装饰器、 结果类型、defer 清理、哨兵值与零值的比较。
  • references/resilience-and-types.md — 带抖动的重试、熔断器、 隔离舱、解析而非校验类型、状态类型。

仅当下方摘要不足时才阅读参考文件。

模式选择

需求模式参考资料
带有许多可选设置的构造函数函数选项creation-patterns.md
从文件/环境变量加载配置,且字段大多必需配置结构体creation-patterns.md
在创建时强制执行不变量返回错误的构造函数creation-patterns.md
根据运行时配置选择实现返回接口的工厂creation-patterns.md
在运行时切换简单行为通过函数类型实现策略behavioral-patterns.md
在运行时切换复杂行为通过接口实现策略behavioral-patterns.md
包装横切关注点(日志、缓存、指标)中间件/装饰器behavioral-patterns.md
并发流水线中的值或错误Result[T] 结构体behavioral-patterns.md
依赖项发生暂时性故障带退避和抖动的重试resilience-and-types.md
依赖项不可用熔断器、隔离舱resilience-and-types.md
只校验一次,之后即可信任解析为字段未导出的类型resilience-and-types.md
用多个布尔值描述同一生命周期显式定义状态转换的状态类型resilience-and-types.md

1. 函数选项(要点)

type Option func(*Server)

func WithAddr(addr string) Option {
    return func(s *Server) { s.addr = addr }
}

func NewServer(opts ...Option) *Server {
    s := &Server{
        addr:        ":8080", // sensible defaults first
        readTimeout: 5 * time.Second,
        logger:      slog.Default(),
    }
    for _, opt := range opts {
        opt(s)
    }
    return s
}

srv := NewServer(WithAddr(":9090"))

适用场景:有许多带合理默认值的可选参数,API 会随时间演进(新增选项不会破坏调用方),选项需要校验。 如果大多数字段都是必需的,或者配置从文件/环境变量反序列化而来,则应改用普通配置结构体。

2. 构造函数规则

  • 每个带有不变量的导出类型都需要构造函数。
  • 校验必需的依赖项;返回错误,不要触发 panic:
// ✅ Good — constructor enforces invariants
func NewUserService(repo UserRepository, logger *slog.Logger) (*UserService, error) {
    if repo == nil {
        return nil, errors.New("user service: nil repository")
    }
    return &UserService{repo: repo, logger: logger}, nil
}

// ❌ Bad — struct literal with no validation
svc := &UserService{} // nil dependencies → panic at runtime

3. 工厂

返回接口,而非具体类型。工厂是唯一知道具体实现的地方:

func NewStore(cfg Config) (Store, error) {
    switch cfg.StoreType {
    case "redis":
        return newRedisStore(cfg.RedisAddr)
    case "memory":
        return newMemoryStore(), nil
    default:
        return nil, fmt.Errorf("unknown store type: %s", cfg.StoreType)
    }
}

4. 中间件链

标准 HTTP 组合模式:

type Middleware func(http.Handler) http.Handler

func Chain(handler http.Handler, middlewares ...Middleware) http.Handler {
    for i := len(middlewares) - 1; i >= 0; i-- {
        handler = middlewares[i](handler)
    }
    return handler
}

handler := Chain(appHandler, Recoverer, RequestID, Logger, Auth)

同样的结构适用于任何接口:将装饰器按如下顺序堆叠: cache → logging → metrics → actual repo (参见 references/behavioral-patterns.md)。

5. 优先考虑零值

优先选择零值有用的类型(sync.Mutex、bytes.Buffer、 nil 切片)。只有当零值作为输入含义不明确时,才考虑使用哨兵包装类型或指针(nil *float64 =“未配置”)。

6. 弹性

  • 每个出站调用都应有来自 ctx 的截止时间(go-context 负责管理预算)。
  • 仅重试可安全重复的操作,并使用指数退避和全抖动、设置上限,且只在一层进行——嵌套重试会产生倍增效应。
  • 依赖项不可用时,熔断器会快速失败;使用维护良好的实现,并在熔断器打开时采用降级方案(缓存值、部分响应)。
  • 隔离舱(每个依赖项一个信号量)可防止单个响应缓慢的依赖项耗尽所有 goroutine 和连接。

7. 让非法状态无法表示

将输入解析一次,得到字段未导出的类型(ParseEmail → Email),这样接收该类型的每个函数都可以信任它。用一种状态类型和用于检查状态转换的方法,替换成组的布尔值(Paid、Shipped、Cancelled)。

第 6–7 节的示例:references/resilience-and-types.md。

应避免的反模式

// ❌ God interface — too many methods
type Service interface {
    GetUser(ctx context.Context, id string) (*User, error)
    CreateUser(ctx context.Context, u *User) error
    DeleteUser(ctx context.Context, id string) error
    ListOrders(ctx context.Context, userID string) ([]Order, error)
    // 20 more methods...
}
// → Split into focused interfaces: UserReader, UserWriter, OrderLister

// ❌ Premature abstraction — interface for one implementation
type UserCache interface {
    Get(key string) (*User, bool)
    Set(key string, user *User)
}
// If there's only ever one implementation, use the concrete type.
// Extract an interface when a second consumer or implementation appears.

// ❌ Java-style inheritance simulation
type BaseService struct{ /* ... */ }
type UserService struct{ BaseService } // embedding is NOT inheritance
// → Use composition: UserService has a dependency, not a parent.

验证清单

  1. 对可选配置的类型使用函数选项
  2. 构造函数验证必需的依赖项并返回错误
  3. 工厂函数返回接口,而不是具体类型
  4. 不使用巨型接口——每个接口有 1–3 个方法
  5. 中间件遵循 func(http.Handler) http.Handler 签名
  6. 装饰器包装接口,而不是具体类型
  7. 使用 defer 清理所有资源(文件、连接、锁)
  8. 零值有意义——无需不必要的初始化
  9. 不进行过早抽象——仅在需要时提取接口
  10. 使用组合而不是嵌入来复用代码
  11. 出站调用设有截止时间;重试带随机抖动、有上限且仅在一层进行
  12. 将经过验证的值和生命周期编码到类型中,而不是重复检查或通过布尔值分散表示

评论

支持 Markdown