关注

Go 的每一个框架都在用的设计模式——装饰器模式

Go 的每一个框架都在用的设计模式——装饰器模式

不修改一行源码,如何让函数"无限增强"?揭秘 Go 框架背后的核心设计模式


一、从一个真实问题开始

假设你正在开发一个 HTTP 服务,需要给核心业务函数添加以下功能:

// 核心业务函数
func HandleRequest(w http.ResponseWriter, r *http.Request) {
    // 处理请求...
    w.Write([]byte("Hello, World!"))
}

需求来了:

  1. 添加日志记录(记录每个请求的 URL、耗时)
  2. 添加认证检查(验证 Token)
  3. 添加限流保护(防止恶意请求)
  4. 添加 panic 恢复(防止服务崩溃)
  5. 添加指标监控(Prometheus metrics)

** naive 的做法:**

func HandleRequest(w http.ResponseWriter, r *http.Request) {
    // 1. 日志
    start := time.Now()
    log.Printf("Request: %s", r.URL)
    defer log.Printf("Duration: %v", time.Since(start))
    
    // 2. 认证
    token := r.Header.Get("Authorization")
    if !validateToken(token) {
        http.Error(w, "Unauthorized", http.StatusUnauthorized)
        return
    }
    
    // 3. 限流
    if !limiter.Allow() {
        http.Error(w, "Too Many Requests", http.StatusTooManyRequests)
        return
    }
    
    // 4. Panic 恢复
    defer func() {
        if err := recover(); err != nil {
            log.Printf("Panic recovered: %v", err)
            http.Error(w, "Internal Server Error", http.StatusInternalServerError)
        }
    }()
    
    // 5. 指标监控
    metrics.RequestCounter.Inc()
    
    // 6. 核心业务逻辑(终于看到了!)
    w.Write([]byte("Hello, World!"))
}

问题:

  • ❌ 核心业务逻辑被"横切关注点"淹没
  • ❌ 代码难以测试(每个功能耦合在一起)
  • ❌ 想给其他 handler 添加相同功能?复制粘贴!
  • ❌ 修改任何一个功能都可能影响其他功能

有没有一种方式,能不修改原函数,动态添加这些功能?

答案是:装饰器模式(Decorator Pattern)


二、装饰器模式核心思想

2.1 什么是装饰器模式?

装饰器模式:动态地给对象添加新的功能,而不改变其原有结构。

用 Go 的话说:用一个函数包装另一个函数,在调用前后添加额外逻辑。

2.2 核心结构

┌─────────────────────────────────────────┐
│           原始函数 (Handler)              │
│         func(w, r) { 业务逻辑 }          │
└─────────────────────────────────────────┘
                    │
                    ▼
┌─────────────────────────────────────────┐
│        装饰器 1 (LoggingDecorator)        │
│   func(w, r) { 日志; 调用原始函数 }       │
└─────────────────────────────────────────┘
                    │
                    ▼
┌─────────────────────────────────────────┐
│        装饰器 2 (AuthDecorator)          │
│   func(w, r) { 认证; 调用装饰器 1 }        │
└─────────────────────────────────────────┘
                    │
                    ▼
┌─────────────────────────────────────────┐
│        装饰器 3 (RateLimitDecorator)     │
│   func(w, r) { 限流; 调用装饰器 2 }       │
└─────────────────────────────────────────┘

关键:装饰器本身和被装饰的对象遵循相同的接口!


三、Go 中的装饰器模式实现

3.1 HTTP Handler 装饰器

// 1. 日志装饰器
func LoggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        
        // 调用前:记录请求
        log.Printf(
            "Started %s %s from %s",
            r.Method,
            r.URL.Path,
            r.RemoteAddr,
        )
        
        // 调用原始 handler
        next.ServeHTTP(w, r)
        
        // 调用后:记录耗时
        log.Printf(
            "Completed %s %s in %v",
            r.Method,
            r.URL.Path,
            time.Since(start),
        )
    })
}

// 2. 认证装饰器
func AuthMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        token := r.Header.Get("Authorization")
        
        if token == "" {
            http.Error(w, "Missing authorization token", http.StatusUnauthorized)
            return
        }
        
        if !validateToken(token) {
            http.Error(w, "Invalid authorization token", http.StatusUnauthorized)
            return
        }
        
        // 认证通过,调用下一个 handler
        next.ServeHTTP(w, r)
    })
}

// 3. 限流装饰器
func RateLimitMiddleware(limiter *rate.Limiter) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if !limiter.Allow() {
            http.Error(w, "Rate limit exceeded", http.StatusTooManyRequests)
            return
        }
        
        next.ServeHTTP(w, r)
    })
}

// 4. Panic 恢复装饰器
func RecoveryMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        defer func() {
            if err := recover(); err != nil {
                log.Printf("Panic recovered: %v", err)
                debug.PrintStack()
                http.Error(w, "Internal Server Error", http.StatusInternalServerError)
            }
        }()
        
        next.ServeHTTP(w, r)
    })
}

// 5. 指标监控装饰器
func MetricsMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // 包装 ResponseWriter 以捕获状态码
        rw := &responseWriter{ResponseWriter: w, statusCode: http.StatusOK}
        
        start := time.Now()
        next.ServeHTTP(rw, r)
        duration := time.Since(start)
        
        // 记录指标
        metrics.RequestCounter.WithLabelValues(r.URL.Path, rw.statusCode).Inc()
        metrics.RequestDuration.WithLabelValues(r.URL.Path).Observe(duration.Seconds(

转载自CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/Ring7852/article/details/159323549

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--