mcpField
G
go-patterns

go-patterns

github.com/geoffjay开发836 次安装51 周前更新
代码仓库

关于 go-patterns

go-patterns 是面向 Go 开发者的编程技能,整理现代 Go 惯用法、设计模式与最佳实践。内容涵盖 Go 1.18 及之后的特性,并提供示例和参考资料。

主要功能

  1. 01现代 Go 特性与泛型
  2. 02常见设计模式示例
  3. 03错误与接口处理惯例
  4. 04Context 使用模式
  5. 05Go 编码最佳实践

适用场景

  1. 01编写符合 Go 惯例的代码
  2. 02重构现有 Go 项目
  3. 03审查代码中的惯用法
  4. 04学习泛型和工作区用法

安装

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

$npx skills add https://github.com/geoffjay/claude-plugins --skill go-patterns

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

详细介绍

Go 模式技能

此技能全面介绍现代 Go 模式、惯用写法和最佳实践,特别关注 Go 1.18 及更高版本引入的功能。

适用场景

在以下情况下启用此技能:

  • 编写符合 Go 惯用风格的代码
  • 在 Go 中实现设计模式
  • 使用现代 Go 功能(泛型、模糊测试、工作区)
  • 重构代码,使其更符合惯用风格
  • 教授 Go 最佳实践
  • 审查代码是否符合惯用规范

现代 Go 功能

泛型(Go 1.18+)

类型参数:

// Generic function
func Map[T, U any](slice []T, f func(T) U) []U {
    result := make([]U, len(slice))
    for i, v := range slice {
        result[i] = f(v)
    }
    return result
}

// Usage
numbers := []int{1, 2, 3, 4, 5}
doubled := Map(numbers, func(n int) int { return n * 2 })

类型约束:

// Ordered constraint
type Ordered interface {
    ~int | ~int8 | ~int16 | ~int32 | ~int64 |
    ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 |
    ~float32 | ~float64 | ~string
}

func Min[T Ordered](a, b T) T {
    if a < b {
        return a
    }
    return b
}

// Custom constraints
type Numeric interface {
    ~int | ~int64 | ~float64
}

func Sum[T Numeric](values []T) T {
    var sum T
    for _, v := range values {
        sum += v
    }
    return sum
}

泛型数据结构:

// Generic stack
type Stack[T any] struct {
    items []T
}

func NewStack[T any]() *Stack[T] {
    return &Stack[T]{items: make([]T, 0)}
}

func (s *Stack[T]) Push(item T) {
    s.items = append(s.items, item)
}

func (s *Stack[T]) Pop() (T, bool) {
    if len(s.items) == 0 {
        var zero T
        return zero, false
    }
    item := s.items[len(s.items)-1]
    s.items = s.items[:len(s.items)-1]
    return item, true
}

// Generic map utilities
func Keys[K comparable, V any](m map[K]V) []K {
    keys := make([]K, 0, len(m))
    for k := range m {
        keys = append(keys, k)
    }
    return keys
}

func Values[K comparable, V any](m map[K]V) []V {
    values := make([]V, 0, len(m))
    for _, v := range m {
        values = append(values, v)
    }
    return values
}
工作区(Go 1.18+)

go.work 文件:

go 1.21

use (
    ./service
    ./shared
    ./tools
)

replace example.com/legacy => ./vendor/legacy

优点:

  • 多模块开发
  • 本地依赖覆盖
  • 简化跨模块测试
  • 更好地支持单体仓库

必备 Go 模式

函数选项模式
type Server struct {
    host    string
    port    int
    timeout time.Duration
    logger  *log.Logger
}

type Option func(*Server)

func WithHost(host string) Option {
    return func(s *Server) {
        s.host = host
    }
}

func WithPort(port int) Option {
    return func(s *Server) {
        s.port = port
    }
}

func WithTimeout(timeout time.Duration) Option {
    return func(s *Server) {
        s.timeout = timeout
    }
}

func WithLogger(logger *log.Logger) Option {
    return func(s *Server) {
        s.logger = logger
    }
}

func NewServer(opts ...Option) *Server {
    s := &Server{
        host:    "localhost",
        port:    8080,
        timeout: 30 * time.Second,
        logger:  log.Default(),
    }

    for _, opt := range opts {
        opt(s)
    }

    return s
}

// Usage
server := NewServer(
    WithHost("0.0.0.0"),
    WithPort(3000),
    WithTimeout(60 * time.Second),
)
构建器模式
type Query struct {
    table      string
    where      []string
    orderBy    string
    limit      int
    offset     int
}

type QueryBuilder struct {
    query Query
}

func NewQueryBuilder(table string) *QueryBuilder {
    return &QueryBuilder{
        query: Query{table: table},
    }
}

func (b *QueryBuilder) Where(condition string) *QueryBuilder {
    b.query.where = append(b.query.where, condition)
    return b
}

func (b *QueryBuilder) OrderBy(field string) *QueryBuilder {
    b.query.orderBy = field
    return b
}

func (b *QueryBuilder) Limit(limit int) *QueryBuilder {
    b.query.limit = limit
    return b
}

func (b *QueryBuilder) Offset(offset int) *QueryBuilder {
    b.query.offset = offset
    return b
}

func (b *QueryBuilder) Build() Query {
    return b.query
}

// Usage
query := NewQueryBuilder("users").
    Where("age > 18").
    Where("active = true").
    OrderBy("created_at DESC").
    Limit(10).
    Offset(20).
    Build()
策略模式
// Strategy interface
type PaymentStrategy interface {
    Pay(amount float64) error
}

// Concrete strategies
type CreditCardPayment struct {
    cardNumber string
}

func (c *CreditCardPayment) Pay(amount float64) error {
    fmt.Printf("Paying $%.2f with credit card %s\n", amount, c.cardNumber)
    return nil
}

type PayPalPayment struct {
    email string
}

func (p *PayPalPayment) Pay(amount float64) error {
    fmt.Printf("Paying $%.2f with PayPal account %s\n", amount, p.email)
    return nil
}

type CryptoPayment struct {
    walletAddress string
}

func (c *CryptoPayment) Pay(amount float64) error {
    fmt.Printf("Paying $%.2f to wallet %s\n", amount, c.walletAddress)
    return nil
}

// Context
type PaymentProcessor struct {
    strategy PaymentStrategy
}

func NewPaymentProcessor(strategy PaymentStrategy) *PaymentProcessor {
    return &PaymentProcessor{strategy: strategy}
}

func (p *PaymentProcessor) ProcessPayment(amount float64) error {
    return p.strategy.Pay(amount)
}

// Usage
processor := NewPaymentProcessor(&CreditCardPayment{cardNumber: "1234-5678"})
processor.ProcessPayment(100.00)

processor = NewPaymentProcessor(&PayPalPayment{email: "[email protected]"})
processor.ProcessPayment(50.00)
观察者模式
type Observer interface {
    Update(event Event)
}

type Event struct {
    Type string
    Data interface{}
}

type Subject struct {
    observers []Observer
}

func (s *Subject) Attach(observer Observer) {
    s.observers = append(s.observers, observer)
}

func (s *Subject) Detach(observer Observer) {
    for i, obs := range s.observers {
        if obs == observer {
            s.observers = append(s.observers[:i], s.observers[i+1:]...)
            break
        }
    }
}

func (s *Subject) Notify(event Event) {
    for _, observer := range s.observers {
        observer.Update(event)
    }
}

// Concrete observer
type Logger struct {
    name string
}

func (l *Logger) Update(event Event) {
    fmt.Printf("[%s] Received event: %s\n", l.name, event.Type)
}

// Usage
subject := &Subject{}
logger1 := &Logger{name: "Logger1"}
logger2 := &Logger{name: "Logger2"}

subject.Attach(logger1)
subject.Attach(logger2)

subject.Notify(Event{Type: "UserCreated", Data: "user123"})

Go 惯用模式

错误处理

哨兵错误:

var (
    ErrNotFound     = errors.New("resource not found")
    ErrUnauthorized = errors.New("unauthorized access")
    ErrInvalidInput = errors.New("invalid input")
)

func GetUser(id string) (*User, error) {
    if id == "" {
        return nil, ErrInvalidInput
    }

    user := findUser(id)
    if user == nil {
        return nil, ErrNotFound
    }

    return user, nil
}

// Check with errors.Is
if errors.Is(err, ErrNotFound) {
    // Handle not found
}

自定义错误类型:

type ValidationError struct {
    Field   string
    Message string
}

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

// Check with errors.As
var valErr *ValidationError
if errors.As(err, &valErr) {
    fmt.Printf("Validation failed: %s\n", valErr.Field)
}

错误包装:

func ProcessUser(id string) error {
    user, err := GetUser(id)
    if err != nil {
        return fmt.Errorf("process user: %w", err)
    }

    if err := ValidateUser(user); err != nil {
        return fmt.Errorf("validate user %s: %w", id, err)
    }

    return nil
}
接口模式

小型接口:

// Good: Small, focused interfaces
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
}

// Compose interfaces
type ReadWriteCloser interface {
    Reader
    Writer
    Closer
}

接口隔离:

// Instead of one large interface
type Repository interface {
    Create(ctx context.Context, user *User) error
    Read(ctx context.Context, id string) (*User, error)
    Update(ctx context.Context, user *User) error
    Delete(ctx context.Context, id string) error
    List(ctx context.Context) ([]*User, error)
    Search(ctx context.Context, query string) ([]*User, error)
}

// Better: Separate interfaces
type UserCreator interface {
    Create(ctx context.Context, user *User) error
}

type UserReader interface {
    Read(ctx context.Context, id string) (*User, error)
    List(ctx context.Context) ([]*User, error)
}

type UserUpdater interface {
    Update(ctx context.Context, user *User) error
}

type UserDeleter interface {
    Delete(ctx context.Context, id string) error
}

type UserSearcher interface {
    Search(ctx context.Context, query string) ([]*User, error)
}
Context 模式

正确使用 Context:

func FetchData(ctx context.Context, url string) ([]byte, error) {
    // Create request with context
    req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
    if err != nil {
        return nil, fmt.Errorf("create request: %w", err)
    }

    // Check for cancellation before expensive operation
    select {
    case <-ctx.Done():
        return nil, ctx.Err()
    default:
    }

    // Execute request
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return nil, fmt.Errorf("execute request: %w", err)
    }
    defer resp.Body.Close()

    return io.ReadAll(resp.Body)
}

// Context with timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

data, err := FetchData(ctx, "https://api.example.com/data")

Context 值:

type contextKey string

const (
    requestIDKey contextKey = "requestID"
    userIDKey    contextKey = "userID"
)

func WithRequestID(ctx context.Context, requestID string) context.Context {
    return context.WithValue(ctx, requestIDKey, requestID)
}

func GetRequestID(ctx context.Context) (string, bool) {
    requestID, ok := ctx.Value(requestIDKey).(string)
    return requestID, ok
}

func WithUserID(ctx context.Context, userID string) context.Context {
    return context.WithValue(ctx, userIDKey, userID)
}

func GetUserID(ctx context.Context) (string, bool) {
    userID, ok := ctx.Value(userIDKey).(string)
    return userID, ok
}

最佳实践

  1. 接受接口,返回结构体
  2. 让零值也能发挥作用
  3. 优先使用组合,而非继承
  4. 显式处理错误
  5. 使用 defer 进行清理
  6. 对于读操作密集型工作负载,优先使用 sync.RWMutex
  7. 使用 context 进行取消和超时控制
  8. 保持接口精简
  9. 为导出的标识符编写文档
  10. 使用 go fmt 和 go vet

资源

更多模式和示例请见 assets/ 目录:

  • examples/ - 完整代码示例
  • patterns/ - 设计模式实现
  • antipatterns/ - 应避免的常见错误

请参阅 references/ 目录,了解:

  • Go 官方文档链接
  • Effective Go 指南
  • Go 格言
  • 社区最佳实践

评论

支持 Markdown