贝利信息

Golang结构体组合实现装饰器模式

日期:2026-01-09 00:00 / 作者:P粉602998670
Go中无装饰器语法,但可通过结构体嵌入(尤其指针嵌入)模拟装饰器模式:嵌入原类型并重写方法以增强行为;需统一接口、避免nil指针、注意初始化顺序与生命周期。

Go 里没有装饰器语法,但结构体嵌入能模拟行为增强

Go 语言本身不支持 Python 那种 @decorator 语法,也没有 Java 的注解 + AOP 框架。所谓“装饰器模式”在 Go 中本质是:**用组合代替继承,通过嵌入(embedding)已有类型,再覆盖或扩展其方法**。关键不是名字,而是能否在不修改原类型的前提下,动态添加职责(比如加日志、重试、熔断)。

嵌入匿名字段是核心,方法集规则决定能否“透明装饰”

要让装饰行为对调用方无感,必须满足:被装饰类型的方法必须出现在装饰器的可导出方法集中。这取决于嵌入方式:

type Foo struct{}

func (*Foo) Do() string { return "original" }

type LoggingFoo struct {
    *Foo // 必须是指针嵌入,否则无法调用 *Foo.Do()
}

func (l *LoggingFoo) Do() string {
    fmt.Println("before Do")
    result := l.Foo.Do()
    fmt.Println("after Do")
    return result
}

装饰器链式调用需手动拼接,不能自动叠加

Python 装饰器可以堆叠写成 @log @retry @cache,Go 没有这种语法糖。多个装饰逻辑只能靠手动嵌套构造:

type Service interface {
    Do() string
}

type CacheService struct {
    next Service // 接口,非具体类型
}

func (c *CacheService) Do() string {
    // 先查缓存...
    return c.next.Do() // 调用下一层
}

别忽略零值与初始化顺序问题

结构体嵌入后,如果装饰器字段未显式初始化,会触发零值行为,常见陷阱:

真正难的不是写一个装饰器,而是确保整条链上每个环节都正确处理了嵌入字段生命周期、错误传播、上下文传递——这些细节不会报编译错误,但会在压测时突然暴露。