导航菜单

Go 的面向对象

Go 语言与传统面向对象语言(如 Java、C++)有着根本性的不同。Go 没有 class、没有继承、没有 this,而是通过 结构体方法接口 以一种更简洁的方式实现面向对象的核心理念。

Go 面向对象的设计哲学

Go 采用 组合优于继承(Composition over Inheritance) 的设计哲学,通过结构体嵌入和接口组合来实现代码复用和多态,避免了传统继承带来的复杂层次结构。

组合优于继承

结构体嵌入与字段提升

Go 通过 嵌入(Embedding) 来实现组合,被嵌入类型的字段和方法会被 提升(Promoted) 到外层结构体,可以直接访问。

package main

import "fmt"

// Base 基础组件
type Base struct {
    Name string
}

func (b Base) Speak() string {
    return "我的名字是 " + b.Name
}

// Dog 通过嵌入 Base 来复用其字段和方法
type Dog struct {
    Base        // 匿名嵌入
    Breed string // 显式字段
}

func (d Dog) Bark() string {
    return "汪汪!"
}

func main() {
    d := Dog{
        Base:  Base{Name: "旺财"},
        Breed: "柴犬",
    }

    // 字段提升:可以直接访问嵌入结构的字段
    fmt.Println(d.Name) // 输出: 旺财

    // 方法提升:可以直接调用嵌入结构的方法
    fmt.Println(d.Speak()) // 输出: 我的名字是 旺财
    fmt.Println(d.Bark())  // 输出: 汪汪!
}
type Dog struct {
    Base
    Name string // 覆盖 Base.Name
}

func main() {
    d := Dog{
        Base: Base{Name: "BaseName"},
        Name: "DogName",
    }
    fmt.Println(d.Name)       // DogName(外层优先)
    fmt.Println(d.Base.Name)  // BaseName(显式访问嵌入层)
}

嵌入 vs 传统继承对比

特性Go 嵌入Java/C++ 继承
关系类型组合(has-a)继承(is-a)
耦合度松耦合紧耦合
多重嵌入✅ 支持多个匿名嵌入⚠️ Java 不支持多重继承
方法覆盖通过同名方法遮蔽通过 @Override 重写
运行时类型无类型层级有类型层级和向上转型

方法集规则

方法集决定了一个类型的值或指针可以调用哪些方法,也直接影响 接口实现 的判定。

方法集(Method Set)

Go 语言中,每个类型都有两个方法集:

  • T 的方法集:只包含所有 值接收者 方法
  • *T 的方法集:包含所有值接收者方法 + 所有 指针接收者 方法

值接收者 vs 指针接收者

type Animal struct {
    Name string
}

// 值接收者:不会修改原始数据
func (a Animal) GetName() string {
    return a.Name
}

// 指针接收者:可以修改原始数据
func (a *Animal) SetName(name string) {
    a.Name = name
}

对接口实现的影响

type Describer interface {
    Describe() string
}

type Person struct {
    Name string
}

// 值接收者实现接口
func (p Person) Describe() string {
    return "我是 " + p.Name
}

// 指针接收者实现接口
func (p *Person) Rename(name string) {
    p.Name = name
}

func main() {
    // Person 实现了 Describer
    var d1 Describer = Person{Name: "Alice"} // ✅ 值可以赋值

    // *Person 也实现了 Describer(包含值接收者方法)
    var d2 Describer = &Person{Name: "Bob"} // ✅ 指针可以赋值
}

Go 面向对象 vs Java/C++ 面向对象

Go 的面向对象是一种 轻量级 的面向对象,没有传统 OOP 语言中的许多概念:

概念Java/C++Go
类定义classstruct
继承extends / : public结构体嵌入
构造函数Constructor()工厂函数 NewXxx()
this / self隐式可用显式命名(通常用接收者变量名)
封装public / private / protected首字母大小写控制
方法重载支持不支持
泛型(早期)支持Go 1.18+ 支持
抽象类支持,用接口替代
// Go 风格的"构造函数":工厂函数
type Service struct {
    host string
    port int
}

// NewService 是 Service 的工厂函数
// host 和 port 小写,包外不可访问(封装)
func NewService(host string, port int) *Service {
    return &Service{
        host: host,
        port: port,
    }
}

func (s *Service) Start() error {
    // 启动逻辑
    return nil
}

封装——通过包和首字母大小写实现访问控制

Go 没有传统的访问修饰符(public/private/protected),而是通过 标识符首字母大小写 来控制访问权限:

Go 的访问控制规则
  • 首字母大写导出(Exported),包外可访问(相当于 public
  • 首字母小写未导出(Unexported),仅包内可访问(相当于 private
  • Go 没有 protected 级别
package account

// Account 是导出的类型
type Account struct {
    Owner   string  // 导出字段——包外可读写
    balance float64 // 未导出字段——仅包内可访问
}

// NewAccount 工厂函数
func NewAccount(owner string, initialBalance float64) *Account {
    return &Account{
        Owner:   owner,
        balance: initialBalance,
    }
}

// Balance 通过方法暴露未导出字段(getter)
func (a *Account) Balance() float64 {
    return a.balance
}

// Deposit 通过方法控制对未导出字段的修改
func (a *Account) Deposit(amount float64) error {
    if amount <= 0 {
        return fmt.Errorf("存款金额必须大于零")
    }
    a.balance += amount
    return nil
}

// Withdraw 通过方法添加业务逻辑校验
func (a *Account) Withdraw(amount float64) error {
    if amount > a.balance {
        return fmt.Errorf("余额不足")
    }
    a.balance -= amount
    return nil
}
package main

import "account"

func main() {
    acc := account.NewAccount("张三", 1000.0)
    fmt.Println(acc.Owner)    // ✅ 导出字段可直接访问
    // fmt.Println(acc.balance) // ❌ 编译错误:未导出字段

    // 必须通过方法访问
    fmt.Println(acc.Balance())           // 1000
    acc.Deposit(500)                     // ✅ 通过方法修改
    acc.Withdraw(200)                    // ✅ 带校验的修改
    fmt.Println(acc.Balance())           // 1300
}

多态——通过接口实现运行时多态

Go 的多态完全依赖 接口(Interface)隐式实现(Duck Typing) 实现。

package main

import "fmt"

// Shape 定义形状接口
type Shape interface {
    Area() float64
    Perimeter() float64
}

// Circle 实现了 Shape 接口(隐式实现,无需 implements 关键字)
type Circle struct {
    Radius float64
}

func (c Circle) Area() float64 {
    return 3.14159 * c.Radius * c.Radius
}

func (c Circle) Perimeter() float64 {
    return 2 * 3.14159 * c.Radius
}

// Rectangle 也实现了 Shape 接口
type Rectangle struct {
    Width, Height float64
}

func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

func (r Rectangle) Perimeter() float64 {
    return 2 * (r.Width + r.Height)
}

// PrintShape 接受接口类型——实现多态
func PrintShape(s Shape) {
    fmt.Printf("面积: %.2f, 周长: %.2f\n", s.Area(), s.Perimeter())
}

func main() {
    shapes := []Shape{
        Circle{Radius: 5},
        Rectangle{Width: 4, Height: 6},
    }

    // 运行时多态:同一个函数,不同的行为
    for _, s := range shapes {
        PrintShape(s)
    }
}

设计模式在 Go 中的应用

策略模式(Strategy Pattern)

策略模式允许在运行时切换算法。Go 中通过 接口 + 函数值 两种方式实现。

package main

import (
    "fmt"
    "strings"
)

// SortStrategy 排序策略接口
type SortStrategy interface {
    Sort(data []int) []int
}

// BubbleSort 冒泡排序策略
type BubbleSort struct{}

func (b BubbleSort) Sort(data []int) []int {
    result := make([]int, len(data))
    copy(result, data)
    n := len(result)
    for i := 0; i < n-1; i++ {
        for j := 0; j < n-i-1; j++ {
            if result[j] > result[j+1] {
                result[j], result[j+1] = result[j+1], result[j]
            }
        }
    }
    return result
}

// QuickSort 快速排序策略
type QuickSort struct{}

func (q QuickSort) Sort(data []int) []int {
    if len(data) <= 1 {
        return data
    }
    pivot := data[0]
    var left, right []int
    for _, v := range data[1:] {
        if v <= pivot {
            left = append(left, v)
        } else {
            right = append(right, v)
        }
    }
    result := append(QuickSort{}.Sort(left), pivot)
    result = append(result, QuickSort{}.Sort(right)...)
    return result
}

// Context 持有策略引用
type Context struct {
    strategy SortStrategy
}

func NewContext(strategy SortStrategy) *Context {
    return &Context{strategy: strategy}
}

func (c *Context) SetStrategy(strategy SortStrategy) {
    c.strategy = strategy
}

func (c *Context) ExecuteSort(data []int) []int {
    return c.strategy.Sort(data)
}

func main() {
    data := []int{64, 34, 25, 12, 22, 11, 90}

    ctx := NewContext(BubbleSort{})
    fmt.Println("冒泡排序:", ctx.ExecuteSort(data))

    ctx.SetStrategy(QuickSort{})
    fmt.Println("快速排序:", ctx.ExecuteSort(data))
}

工厂模式(Factory Pattern)

Go 推荐使用 工厂函数 而非传统的工厂类:

package main

import "fmt"

// 数据库接口
type Database interface {
    Connect() error
    Query(sql string) (string, error)
    Close() error
}

// MySQL 实现
type MySQL struct {
    host     string
    port     int
    password string
}

func (m *MySQL) Connect() error {
    fmt.Printf("连接 MySQL %s:%d\n", m.host, m.port)
    return nil
}

func (m *MySQL) Query(sql string) (string, error) {
    return fmt.Sprintf("MySQL 查询结果: %s", sql), nil
}

func (m *MySQL) Close() error {
    fmt.Println("关闭 MySQL 连接")
    return nil
}

// PostgreSQL 实现
type PostgreSQL struct {
    url string
}

func (p *PostgreSQL) Connect() error {
    fmt.Printf("连接 PostgreSQL: %s\n", p.url)
    return nil
}

func (p *PostgreSQL) Query(sql string) (string, error) {
    return fmt.Sprintf("PostgreSQL 查询结果: %s", sql), nil
}

func (p *PostgreSQL) Close() error {
    fmt.Println("关闭 PostgreSQL 连接")
    return nil
}

// 简单工厂函数
func NewDatabase(driver string) (Database, error) {
    switch driver {
    case "mysql":
        return &MySQL{host: "localhost", port: 3306}, nil
    case "postgres":
        return &PostgreSQL{url: "postgres://localhost:5432"}, nil
    default:
        return nil, fmt.Errorf("不支持的数据库驱动: %s", driver)
    }
}

// 各自的工厂函数(推荐方式)
func NewMySQL(host string, port int) *MySQL {
    return &MySQL{host: host, port: port}
}

func NewPostgreSQL(url string) *PostgreSQL {
    return &PostgreSQL{url: url}
}

func main() {
    // 使用简单工厂
    db, err := NewDatabase("mysql")
    if err != nil {
        panic(err)
    }
    db.Connect()
    result, _ := db.Query("SELECT * FROM users")
    fmt.Println(result)
    db.Close()
}

观察者模式(Observer Pattern)

package main

import "fmt"

// Observer 观察者接口
type Observer interface {
    Update(event string)
}

// Subject 被观察者(事件发布者)
type Subject struct {
    observers []Observer
}

func (s *Subject) Attach(observer Observer) {
    s.observers = append(s.observers, observer)
}

func (s *Subject) Detach(observer Observer) {
    for i, obs := range s.observers {
        if obs == observer {
            s.observers = append(s.observers[:i], s.observers[i+1:]...)
            break
        }
    }
}

func (s *Subject) Notify(event string) {
    for _, obs := range s.observers {
        obs.Update(event)
    }
}

// 具体观察者:邮件通知
type EmailNotifier struct {
    Email string
}

func (e EmailNotifier) Update(event string) {
    fmt.Printf("📧 发送邮件到 %s: 事件 [%s]\n", e.Email, event)
}

// 具体观察者:短信通知
type SMSNotifier struct {
    Phone string
}

func (s SMSNotifier) Update(event string) {
    fmt.Printf("📱 发送短信到 %s: 事件 [%s]\n", s.Phone, event)
}

// 具体观察者:日志记录
type LogNotifier struct{}

func (l LogNotifier) Update(event string) {
    fmt.Printf("📝 记录日志: 事件 [%s]\n", event)
}

func main() {
    subject := &Subject{}

    // 注册观察者
    email := EmailNotifier{Email: "user@example.com"}
    sms := SMSNotifier{Phone: "13800138000"}
    log := LogNotifier{}

    subject.Attach(email)
    subject.Attach(sms)
    subject.Attach(log)

    // 发布事件
    fmt.Println("--- 发布事件: 用户注册 ---")
    subject.Notify("用户注册")

    fmt.Println("\n--- 取消短信通知后发布事件: 用户下单 ---")
    subject.Detach(sms)
    subject.Notify("用户下单")
}

Go 语言用最简洁的语法实现了面向对象编程的核心价值:封装、组合、多态。没有 class、没有继承、没有 this 并不是缺陷,而是刻意为之的设计选择——用 更少的机制 实现同样的目标,让代码更清晰、更易维护。

搜索