0Pricing
Go Academy · 课时

组合优于继承

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 反馈 — 无需本地设置。

此课程中的所有课时

  1. 嵌入结构体
  2. 嵌入接口
  3. 方法提升
  4. 组合优于继承
← 返回 Go Academy