Go 的每一个框架都在用的设计模式——装饰器模式
不修改一行源码,如何让函数"无限增强"?揭秘 Go 框架背后的核心设计模式
一、从一个真实问题开始
假设你正在开发一个 HTTP 服务,需要给核心业务函数添加以下功能:
// 核心业务函数
func HandleRequest(w http.ResponseWriter, r *http.Request) {
// 处理请求...
w.Write([]byte("Hello, World!"))
}
需求来了:
- 添加日志记录(记录每个请求的 URL、耗时)
- 添加认证检查(验证 Token)
- 添加限流保护(防止恶意请求)
- 添加 panic 恢复(防止服务崩溃)
- 添加指标监控(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



