没有
extends,没有implements,Go 用一套更简洁的哲学,重新定义了面向对象。
目录
- [1. 封装:struct + 包级访问控制](#1. 封装:struct + 包级访问控制 "#1-%E5%B0%81%E8%A3%85struct--%E5%8C%85%E7%BA%A7%E8%AE%BF%E9%97%AE%E6%8E%A7%E5%88%B6")
- [2. 继承:Go 没有继承,只有组合](#2. 继承:Go 没有继承,只有组合 "#2-%E7%BB%A7%E6%89%BFgo-%E6%B2%A1%E6%9C%89%E7%BB%A7%E6%89%BF%E5%8F%AA%E6%9C%89%E7%BB%84%E5%90%88")
- [3. 多态:隐式接口与 Duck Typing](#3. 多态:隐式接口与 Duck Typing "#3-%E5%A4%9A%E6%80%81%E9%9A%90%E5%BC%8F%E6%8E%A5%E5%8F%A3%E4%B8%8E-duck-typing")
- [4. 完整实战:一个可运行的动物园](#4. 完整实战:一个可运行的动物园 "#4-%E5%AE%8C%E6%95%B4%E5%AE%9E%E6%88%98%E4%B8%80%E4%B8%AA%E5%8F%AF%E8%BF%90%E8%A1%8C%E7%9A%84%E5%8A%A8%E7%89%A9%E5%9B%AD")
- [5. 三要素对比总表](#5. 三要素对比总表 "#5-%E4%B8%89%E8%A6%81%E7%B4%A0%E5%AF%B9%E6%AF%94%E6%80%BB%E8%A1%A8")
- [6. Go 的设计哲学](#6. Go 的设计哲学 "#6-go-%E7%9A%84%E8%AE%BE%E8%AE%A1%E5%93%B2%E5%AD%A6")
- [7. 结语](#7. 结语 "#7-%E7%BB%93%E8%AF%AD")
1. 封装:struct + 包级访问控制
Go 用 struct 代替 class,用首字母大小写 控制访问权限,而非 public/private 关键字。
go
package animal
// Cat 结构体
type Cat struct {
Name string // 大写开头 = public,包外可访问
age int // 小写开头 = private,仅包内可访问
}
// 公有方法
func (c *Cat) Run() {
println(c.Name, "在跑")
}
// 私有方法
func (c *Cat) purr() {
println("咕噜声")
}
与传统 OOP 对比:
| 维度 | Go | Java/C++ |
|---|---|---|
| 访问控制 | 包级(首字母大小写) | 类级(public/private/protected) |
| 载体 | struct |
class |
| 方法绑定 | 可定义在任意自定义类型上 | 必须定义在 class 内部 |
2. 继承:Go 没有继承,只有组合
Go 的设计哲学是**"组合优于继承"(Composition over Inheritance)。通过匿名嵌入(Embedding)**实现代码复用。
go
package main
import "fmt"
type Animal struct {
Age int
}
func (a *Animal) Eat() {
fmt.Println("吃东西,年龄:", a.Age)
}
// Cat 匿名嵌入 Animal,复用其字段和方法
type Cat struct {
Animal // 匿名嵌入,语法糖级组合
Name string
}
func main() {
c := Cat{
Animal: Animal{Age: 3},
Name: "咪咪",
}
c.Eat() // 直接调用 Animal 的方法
fmt.Println(c.Age) // 直接访问 Animal 的字段
}
关键点:
- 嵌入不是继承,底层仍是"有一个"(has-a)关系
- 天然支持多嵌入,彻底规避 C++ 的菱形继承问题
- 外层同名方法会遮蔽内层方法,实现类似重写的效果
3. 多态:隐式接口与 Duck Typing
这是 Go 最独特的设计:你不需要声明实现了某个接口,只要你拥有接口要求的方法,你就自动实现了它。
go
package main
import "fmt"
// 定义接口
type Runner interface {
Run()
}
// === Cat 自动满足 Runner ===
type Cat struct{}
func (c *Cat) Run() { fmt.Println("🐱 猫在跑") }
// === Dog 自动满足 Runner ===
type Dog struct{}
func (d *Dog) Run() { fmt.Println("🐶 狗在跑") }
// === 多态:接收接口,运行时动态分派 ===
func foo(a Runner) { // ← 参数是接口类型
a.Run() // 运行时可能是 Cat,也可能是 Dog
}
func main() {
foo(&Cat{}) // 输出:🐱 猫在跑
foo(&Dog{}) // 输出:🐶 狗在跑
}
与传统 OOP 对比:
| 维度 | Go | Java/C++ |
|---|---|---|
| 实现方式 | 隐式实现(有方法即实现) | 显式实现 (必须 implements/extends) |
| 耦合度 | 接口与实现解耦,可事后定义 | 接口与实现紧耦合,需预先声明 |
| 空接口 | interface{}(即 any)可承载任意类型 |
需显式向上转型到 Object |
| 类型断言 | v, ok := a.(Cat) |
instanceof + 强制转换 |
4. 完整实战:一个可运行的动物园
以下是一个可直接 go run 的完整示例,演示三要素在实战中的协作:
go
package main
import "fmt"
// ===== 接口:定义行为契约 =====
type Runner interface {
Run()
}
type Speaker interface {
Speak() string
}
// ===== 组合基类 =====
type Animal struct {
Name string
}
func (a *Animal) Introduce() {
fmt.Printf("大家好,我是 %s\n", a.Name)
}
// ===== Cat:组合 + 隐式实现接口 =====
type Cat struct {
Animal // 组合复用
}
func (c *Cat) Run() {
fmt.Println(c.Name, "在优雅地跑")
}
func (c *Cat) Speak() string {
return "喵~"
}
// ===== Dog:组合 + 隐式实现接口 =====
type Dog struct {
Animal
}
func (d *Dog) Run() {
fmt.Println(d.Name, "在欢快地跑")
}
func (d *Dog) Speak() string {
return "汪!"
}
// ===== 多态函数 =====
func Race(r Runner) {
fmt.Print("比赛开始:")
r.Run()
}
func Concert(s Speaker) {
fmt.Println("演唱:", s.Speak())
}
func main() {
cat := &Cat{Animal{Name: "咪咪"}}
dog := &Dog{Animal{Name: "旺财"}}
// 多态调用
Race(cat) // 比赛开始:咪咪 在优雅地跑
Race(dog) // 比赛开始:旺财 在欢快地跑
Concert(cat) // 演唱: 喵~
Concert(dog) // 演唱: 汪!
// 组合复用
cat.Introduce() // 大家好,我是 咪咪
}
5. 三要素对比总表
| 三要素 | Go 语言实现 | 传统 OOP(Java/C++) |
|---|---|---|
| 封装 | struct + 包级访问控制 |
class + public/private/protected |
| 继承 | ❌ 无继承,用匿名嵌入/组合 | ✅ extends 显式继承 |
| 多态 | ✅ 隐式接口,Duck Typing | 显式 implements / 抽象类 |
6. Go 的设计哲学
- 正交性 :
struct、interface、method是正交概念,可任意组合,不像class是一锅大杂烩 - 隐式接口降低耦合:调用方定义接口,实现方零感知,真正做到面向接口编程
- 组合优于继承:消灭脆弱基类、菱形继承等经典 OOP 难题
7. 结语
Go 的面向对象不是"阉割版",而是经过深思熟虑的精简版。它保留了 OOP 中最实用的部分(封装、多态),去掉了最有争议的部分(继承层次),用组合和隐式接口让代码更松耦合、更易测试。
如果你是从 Java/C++ 转来 Go 的开发者,放下"找 class 和 extends"的执念,拥抱组合与接口,你会发现代码反而更干净。