
在 go 中,通过为嵌入结构体定义同名方法并显式调用嵌入字段的方法(如 `b.base.set(i)`)来实现“重写”效果,在不修改原始结构体的前提下增强行为(如记录状态变更)。
Go 并不支持传统面向对象语言中的继承或多态,也没有 super() 关键字,但其嵌入(embedding)机制提供了类似的能力——组合优于继承。当一个结构体(如 Sub)嵌入另一个结构体(如 Base)时,Base 的字段和方法会“提升”为 Sub 的成员;若 Sub 定义了与 Base 同签名的方法,则该方法会隐藏(shadow) 嵌入的版本,从而实现语义上的“重写”。
关键在于:被隐藏的方法并未消失,仍可通过显式路径访问(如 s.Base.Set(1)),而真正的增强逻辑应放在 Sub 的方法中,并主动委托给嵌入字段:
func (s *Sub) Set(i int) {
s.Base.Set(i) // 显式调用嵌入结构体的方法,等效于“super.Set()”
s.changed = true
}这样,所有对 s.Set(...) 的调用都会触发状态标记,而 Base 本身保持完全不变——符合开闭原则(对扩展开放,对修改关闭)。
⚠️ 注意事项:
- 不要误用 s.Base.Set() 替代 s.Set():前者绕过增强逻辑,直接操作底层,破坏封装;
- 类型断言或接口赋值时需谨慎:var b *Base = &s.Base 得到的是纯 Base 指针,调用 b.Set() 永远不会触发 Sub 的逻辑;
- 若需对外暴露统一行为(即用户无论持 *Base 还是 *Sub 都能触发监控),应避免暴露 *Base 指针,转而设计接口(如 type Setter interface { Set(int) }),让 *Sub 实现它,并隐藏 Base 的直接使用。
总结:Go 中的“方法重写”本质是显式委托 + 方法隐藏。合理利用嵌入与显式调用,即可在零侵入前提下为现有类型添加可观测性、日志、验证等横切关注点。










