
本文介绍 go 中实现 dry(don’t repeat yourself)的核心技巧——利用结构体嵌入(composition)复用公共行为,使不同类型仅需实现差异化逻辑,而无需重复编写相同接口方法。
本文介绍 go 中实现 dry(don’t repeat yourself)的核心技巧——利用结构体嵌入(composition)复用公共行为,使不同类型仅需实现差异化逻辑,而无需重复编写相同接口方法。
在 Go 语言中,由于不支持继承,但提供了组合优于继承(Composition over Inheritance) 的哲学,我们可以通过嵌入(embedding)小型、职责单一的结构体来共享通用行为,从而优雅地避免代码重复。
回到你的 Animal 接口场景:
type Animal interface {
Kingdom() string
Phylum() string
Family() string
}Wolf 和 Tiger 都属于 Animalia 界和 Chordata 门,仅 Family 不同(Canidae vs Felidae)。若为每个类型都重写全部三个方法,显然违背 DRY 原则。正确解法是将共性逻辑提取为独立结构体,并通过匿名字段嵌入:
// 共享行为封装为轻量结构体(无字段,仅方法)
type Animalia struct{}
func (Animalia) Kingdom() string { return "Animalia" }
type Chordata struct{}
func (Chordata) Phylum() string { return "Chordata" }
type Canidae struct{}
func (Canidae) Family() string { return "Canidae" }
type Felidae struct{}
func (Felidae) Family() string { return "Felidae" }
// 具体类型仅嵌入所需组件
type Wolf struct {
Animalia
Chordata
Canidae
}
type Tiger struct {
Animalia
Chordata
Felidae
}✅ 这样,Wolf{} 自动获得 Kingdom()、Phylum() 和 Family() 方法(来自嵌入字段),Tiger 同理——你只需为每个类型定义其特有的家族结构体,无需重复实现公共方法。
运行验证:
func main() {
w := Wolf{}
t := Tiger{}
fmt.Println(w.Kingdom(), w.Phylum(), w.Family()) // Animalia Chordata Canidae
fmt.Println(t.Kingdom(), t.Phylum(), t.Family()) // Animalia Chordata Felidae
}? 关键要点与注意事项:
- 嵌入结构体必须是导出类型(首字母大写),否则其方法不会被提升(promoted)到外层类型;
- 若多个嵌入字段实现了同一方法(如两个结构体都有
Kingdom()),编译器会报错,强制你显式覆盖或重构——这反而是类型安全的体现; - 此模式天然支持“组合爆炸”式扩展:例如新增
Bird类型,只需嵌入Animalia + Chordata + Aves,完全解耦; - 对于含状态的逻辑(如需维护计数器、缓存等),嵌入结构体可包含字段,方法操作自身字段,依然保持复用性;
- 避免过度拆分:每个嵌入单元应代表一个清晰、稳定的抽象层次(如分类学层级),而非为复用而强行切分。
通过这种基于组合的抽象,Go 在无继承的前提下,同样能构建高内聚、低耦合、符合 DRY 原则的可维护代码体系。

















