组合优于继承
Go 的设计理念
组合优于继承 是 CoddyKit 上的免费 Go Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Go Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Go Academy 课程共包含 4 节课。
Go 设计理念
Go 有意省略了类继承。符合 Go 惯用方式的做法是组合:通过组合小型类型和接口来构建行为。
为什么不使用继承
深层继承树很脆弱:基类的变更会以不可预测的方式层层传播。组合则能让关系保持明确且灵活。
拥有关系优于继承关系
不要将其建模为“管理者是员工”,而应通过嵌入或字段,将其建模为“管理者拥有员工的数据和能力”。
package main
import "fmt"
type Employee struct{ Name string }
func (e Employee) Work() string { return e.Name + " works" }
type Manager struct {
Employee
Reports int
}
func main() {
m := Manager{Employee{"Lin"}, 3}
fmt.Println(m.Work(), "-", m.Reports, "reports")
}面向接口编程
使用小型接口定义行为,然后接受这些接口。不同实现可以互换,也便于替换或模拟。
package main
import "fmt"
type Notifier interface{ Notify(msg string) }
type Email struct{}
func (Email) Notify(msg string) { fmt.Println("email:", msg) }
func alert(n Notifier, m string) { n.Notify(m) }
func main() {
alert(Email{}, "deploy done")
}替换实现
由于函数依赖于接口,因此您可以传入任意实现,而无需修改函数。
package main
import "fmt"
type Notifier interface{ Notify(msg string) }
type Email struct{}
type SMS struct{}
func (Email) Notify(m string) { fmt.Println("email:", m) }
func (SMS) Notify(m string) { fmt.Println("sms:", m) }
func alert(n Notifier, m string) { n.Notify(m) }
func main() {
alert(Email{}, "hi")
alert(SMS{}, "hi")
}组合行为
嵌入多个小型组件,将独立且可测试的部分组装成一个功能丰富的类型。
package main
import "fmt"
type Logger struct{}
func (Logger) Log(s string) { fmt.Println("log:", s) }
type Cache struct{ data map[string]string }
func (c Cache) Get(k string) string { return c.data[k] }
type Service struct {
Logger
Cache
}
func main() {
s := Service{Cache: Cache{data: map[string]string{"k": "v"}}}
s.Log("started")
fmt.Println(s.Get("k"))
}依赖注入
将依赖项(以接口形式)传入构造函数,而不是将其硬编码。这就是应用于连接过程的组合。
package main
import "fmt"
type Store interface{ Save(s string) }
type MemStore struct{ items []string }
func (m *MemStore) Save(s string) { m.items = append(m.items, s) }
type App struct{ store Store }
func NewApp(s Store) *App { return &App{store: s} }
func main() {
app := NewApp(&MemStore{})
app.store.Save("x")
fmt.Println("saved")
}小接口更有优势
接口越小,满足它的类型就越多,也越容易进行测试。接口越大,抽象就越弱。
避免脆弱的基类
没有继承,就不存在可能被破坏的基类。每个组合部分都能独立演进,从而减少耦合。
接受接口,返回结构体
Go 中常见的一条指导原则是:函数接受接口(输入灵活),返回具体结构体(输出清晰且可直接使用)。
组合检查清单
- 建模为拥有关系,而非继承关系
- 嵌入小型组件
- 依赖小型接口
- 注入依赖项
快速检查
测试您对组合理念的掌握程度。
回顾
您学习了 Go 的组合理念。
- 没有继承;使用嵌入和接口进行组合
- 建模为拥有关系,而非继承关系
- 面向小型接口编程,注入依赖项
- 接受接口,返回结构体
常见问题解答
「组合优于继承」课时是免费的吗?
是的 — 「组合优于继承」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Go Academy 课程的其余内容,请升级到 CoddyKit PRO。 Go Academy 课程共包含 4 节课。
「组合优于继承」这节课中我会学到什么?
Go 的设计理念 你通过在浏览器中直接运行的动手代码来练习 Go Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Go Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Go Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「组合优于继承」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Go Academy 课中编写并运行代码吗?
能。每节 Go Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。