Go语言可通过闭包实现装饰器模式:装饰器为接收函数并返回新函数的高阶函数,支持日志、重试等横切关注点;多个装饰器可链式调用,外层先执行。

Go 语言本身不支持像 Python 那样的语法级装饰器(@decorator),但可以通过函数式编程和闭包,干净地实现 装饰器模式(Decorator Pattern)——即在不修改原始函数逻辑的前提下,动态叠加日志、重试、超时、熔断等横切关注点。
用闭包模拟装饰器
核心思路:让装饰器接收一个函数作为参数,返回一个增强后的新函数。
- 被装饰函数签名需统一(例如
func() error或func(ctx context.Context) (interface{}, error)) - 装饰器本身是高阶函数:输入函数,输出函数
- 多个装饰器可链式调用,顺序决定执行顺序(外层先执行)
基础示例:日志 + 重试装饰器
假设有一个简单操作:
func FetchData() error {
fmt.Println("Fetching data...")
return nil
}
定义两个装饰器:
立即学习“go语言免费学习笔记(深入)”;
- LogDecorator:记录开始与结束时间
- RetryDecorator:失败时最多重试 3 次
实现方式:
func LogDecorator(f func() error) func() error {
return func() error {
fmt.Println("[LOG] Start")
err := f()
fmt.Println("[LOG] Done")
return err
}
}
func RetryDecorator(f func() error, maxRetries int) func() error {
return func() error {
var err error
for i := 0; i <= maxRetries; i++ {
err = f()
if err == nil {
return nil
}
if i < maxRetries {
fmt.Printf("[RETRY] Attempt %d failed: %v\n", i+1, err)
time.Sleep(time.Second)
}
}
return err
}
}
叠加使用:
enhanced := RetryDecorator(LogDecorator(FetchData), 2) enhanced() // 先 log,再 retry 控制
更实用的 HTTP 客户端装饰器链
面向真实场景,比如封装一个带超时、指标统计、错误分类的 HTTP 调用:
- 用
func(context.Context, string) ([]byte, error)统一签名 - TimeoutDecorator:注入 context.WithTimeout
- MetricsDecorator:记录耗时与成功/失败次数(如用 Prometheus Counter/Gauge)
- CircuitBreakerDecorator:简单状态机控制熔断(可基于 github.com/sony/gobreaker)
组合方式自然清晰:
call := CircuitBreakerDecorator(
MetricsDecorator(
TimeoutDecorator(httpGet, 5*time.Second),
"api_fetch"
),
cb
)
小技巧:用结构体统一管理装饰器配置
避免参数过多,可定义装饰器选项结构:
type DecoratorOption struct {
WithTimeout time.Duration
WithRetries int
WithLogger *log.Logger
}
func WithTimeout(d time.Duration) func(*DecoratorOption) {
return func(o *DecoratorOption) { o.WithTimeout = d }
}
func WithRetries(n int) func(*DecoratorOption) {
return func(o *DecoratorOption) { o.WithRetries = n }
}
func BuildDecorator(opts ...func(*DecoratorOption)) func(func() error) func() error {
opt := &DecoratorOption{WithRetries: 1}
for _, apply := range opts {
apply(opt)
}
return func(f func() error) func() error {
return RetryDecorator(TimeoutDecorator(f, opt.WithTimeout), opt.WithRetries)
}
}
调用更简洁:
decorator := BuildDecorator(WithTimeout(3*time.Second), WithRetries(3)) wrapped := decorator(FetchData)
基本上就这些。Go 的装饰器不是语法糖,而是靠闭包 + 函数值 + 显式组合实现的轻量设计,灵活、可控、无反射、零依赖。关键在于统一接口、分层关注点、按需叠加。










