Announcement

👇Official Account👇

图片

Welcome to join the group & private message

Article first/tail QR code

Skip to content

接口参数设计 - 多场景复用的优雅之道

好的接口设计能让代码更易用、易维护。本文介绍 Go 语言中接口参数设计的最佳实践。

一、基础参数设计

1.1 函数选项模式(Functional Options)

go
// 定义选项类型
type ServerOption func(*Server)

// Server 结构体
type Server struct {
    host     string
    port     int
    timeout  time.Duration
    maxConns int
    tls      *tls.Config
}

// 选项函数
func WithHost(host string) ServerOption {
    return func(s *Server) {
        s.host = host
    }
}

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

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

func WithTLS(config *tls.Config) ServerOption {
    return func(s *Server) {
        s.tls = config
    }
}

// 构造函数
func NewServer(opts ...ServerOption) *Server {
    s := &Server{
        host:     "0.0.0.0",
        port:     8080,
        timeout:  30 * time.Second,
        maxConns: 100,
    }
    
    for _, opt := range opts {
        opt(s)
    }
    
    return s
}

// 使用示例
server := NewServer(
    WithHost("localhost"),
    WithPort(9090),
    WithTimeout(60*time.Second),
)

1.2 Builder 模式

go
type QueryBuilder struct {
    table   string
    select_ []string
    where   []Condition
    orderBy []string
    limit   int
    offset  int
}

func NewQueryBuilder(table string) *QueryBuilder {
    return &QueryBuilder{
        table:   table,
        select_: []string{"*"},
        where:   make([]Condition, 0),
        orderBy: make([]string, 0),
    }
}

func (qb *QueryBuilder) Select(columns ...string) *QueryBuilder {
    qb.select_ = columns
    return qb
}

func (qb *QueryBuilder) Where(column string, operator string, value interface{}) *QueryBuilder {
    qb.where = append(qb.where, Condition{column, operator, value})
    return qb
}

func (qb *QueryBuilder) OrderBy(column string, desc bool) *QueryBuilder {
    direction := "ASC"
    if desc {
        direction = "DESC"
    }
    qb.orderBy = append(qb.orderBy, fmt.Sprintf("%s %s", column, direction))
    return qb
}

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

func (qb *QueryBuilder) Build() (string, []interface{}) {
    // 构建 SQL 查询
    return qb.buildQuery()
}

// 使用示例
query, args := NewQueryBuilder("users").
    Select("id", "name", "email").
    Where("age", ">", 18).
    Where("status", "=", "active").
    OrderBy("created_at", true).
    Limit(10).
    Build()

二、泛型参数设计

2.1 泛型函数

go
// 通用过滤函数
func Filter[T any](slice []T, predicate func(T) bool) []T {
    result := make([]T, 0)
    for _, item := range slice {
        if predicate(item) {
            result = append(result, item)
        }
    }
    return result
}

// 通用映射函数
func Map[T, R any](slice []T, transform func(T) R) []R {
    result := make([]R, len(slice))
    for i, item := range slice {
        result[i] = transform(item)
    }
    return result
}

// 使用示例
numbers := []int{1, 2, 3, 4, 5}

// 过滤偶数
evens := Filter(numbers, func(n int) bool {
    return n%2 == 0
})

// 映射为字符串
strings := Map(numbers, func(n int) string {
    return fmt.Sprintf("number-%d", n)
})

2.2 泛型接口

go
// 定义泛型存储接口
type Store[T any] interface {
    Get(id string) (T, error)
    Set(id string, value T) error
    Delete(id string) error
    List() ([]T, error)
}

// 实现
type MemoryStore[T any] struct {
    data map[string]T
    mu   sync.RWMutex
}

func NewMemoryStore[T any]() *MemoryStore[T] {
    return &MemoryStore[T]{
        data: make(map[string]T),
    }
}

func (s *MemoryStore[T]) Get(id string) (T, error) {
    s.mu.RLock()
    defer s.mu.RUnlock()
    
    value, ok := s.data[id]
    if !ok {
        var zero T
        return zero, fmt.Errorf("not found")
    }
    return value, nil
}

func (s *MemoryStore[T]) Set(id string, value T) error {
    s.mu.Lock()
    defer s.mu.Unlock()
    
    s.data[id] = value
    return nil
}

// 使用示例
userStore := NewMemoryStore[User]()
productStore := NewMemoryStore[Product]()

三、参数验证

3.1 结构体验证

go
type CreateUserRequest struct {
    Name     string `json:"name" validate:"required,min=2,max=50"`
    Email    string `json:"email" validate:"required,email"`
    Age      int    `json:"age" validate:"gte=0,lte=150"`
    Password string `json:"password" validate:"required,min=8"`
}

func (r *CreateUserRequest) Validate() error {
    validate := validator.New()
    return validate.Struct(r)
}

// 使用
func createUserHandler(c *gin.Context) {
    var req CreateUserRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }
    
    if err := req.Validate(); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }
    
    // 处理请求
}

3.2 自定义验证器

go
// 自定义验证标签
func init() {
    validate := validator.New()
    
    // 注册自定义验证器
    validate.RegisterValidation("phone", validatePhone)
    validate.RegisterValidation("strong_password", validateStrongPassword)
}

func validatePhone(fl validator.FieldLevel) bool {
    phone := fl.Field().String()
    // 验证手机号格式
    matched, _ := regexp.MatchString(`^1[3-9]\d{9}$`, phone)
    return matched
}

func validateStrongPassword(fl validator.FieldLevel) bool {
    password := fl.Field().String()
    // 至少包含一个大写字母、一个小写字母、一个数字
    hasUpper := regexp.MustCompile(`[A-Z]`).MatchString(password)
    hasLower := regexp.MustCompile(`[a-z]`).MatchString(password)
    hasNumber := regexp.MustCompile(`[0-9]`).MatchString(password)
    
    return hasUpper && hasLower && hasNumber
}

四、多场景复用设计

4.1 通用响应结构

go
// 标准响应结构
type Response[T any] struct {
    Code    int    `json:"code"`
    Message string `json:"message"`
    Data    T      `json:"data,omitempty"`
}

// 分页响应
type PaginatedResponse[T any] struct {
    Code    int    `json:"code"`
    Message string `json:"message"`
    Data    struct {
        List     []T   `json:"list"`
        Total    int64 `json:"total"`
        Page     int   `json:"page"`
        PageSize int   `json:"page_size"`
    } `json:"data"`
}

// 成功响应
func Success[T any](data T) Response[T] {
    return Response[T]{
        Code:    0,
        Message: "success",
        Data:    data,
    }
}

// 错误响应
func Error(code int, message string) Response[any] {
    return Response[any]{
        Code:    code,
        Message: message,
    }
}

// 分页响应
func Paginated[T any](list []T, total int64, page, pageSize int) PaginatedResponse[T] {
    resp := PaginatedResponse[T]{}
    resp.Code = 0
    resp.Message = "success"
    resp.Data.List = list
    resp.Data.Total = total
    resp.Data.Page = page
    resp.Data.PageSize = pageSize
    return resp
}

4.2 通用分页参数

go
// 分页请求参数
type PaginationParams struct {
    Page     int `form:"page" json:"page" binding:"min=1"`
    PageSize int `form:"page_size" json:"page_size" binding:"min=1,max=100"`
}

func (p *PaginationParams) Normalize() {
    if p.Page <= 0 {
        p.Page = 1
    }
    if p.PageSize <= 0 {
        p.PageSize = 10
    }
    if p.PageSize > 100 {
        p.PageSize = 100
    }
}

func (p *PaginationParams) Offset() int {
    return (p.Page - 1) * p.PageSize
}

func (p *PaginationParams) Limit() int {
    return p.PageSize
}

// 排序参数
type SortParams struct {
    SortBy string `form:"sort_by" json:"sort_by"`
    Order  string `form:"order" json:"order" binding:"omitempty,oneof=asc desc"`
}

func (s *SortParams) OrderBy() string {
    if s.SortBy == "" {
        return ""
    }
    
    order := "ASC"
    if s.Order == "desc" {
        order = "DESC"
    }
    
    return fmt.Sprintf("%s %s", s.SortBy, order)
}

// 组合使用
type ListUsersRequest struct {
    PaginationParams
    SortParams
    Name   string `form:"name" json:"name"`
    Status string `form:"status" json:"status"`
}

五、总结

设计模式适用场景优点
函数选项配置类构造函数灵活、可扩展
Builder复杂对象构建清晰、链式调用
泛型通用算法/数据结构类型安全、复用
结构体验证请求参数验证声明式、自动化

好的接口参数设计能让代码更易用、易维护、易测试。

上次更新于: