3. Go面向对象

3.1 核心知识点

结构体(Struct)

type Person struct {
    Name string
    Age  int
}
 
person := Person{Name: "Alice", Age: 25}
person := Person{"Alice", 25}
person.Name = "Bob"

方法(Method)

func (p Person) String() string {
    return fmt.Sprintf("%s (%d years old)", p.Name, p.Age)
}
 
func (p *Person) Birthday() {
    p.Age++
}

接口(Interface)

type Speaker interface {
    Speak() string
}
 
type Dog struct {
    Name string
}
 
func (d Dog) Speak() string {
    return "Woof!"
}
 
var speaker Speaker = Dog{Name: "Buddy"}
fmt.Println(speaker.Speak())

组合(Composition)

type Animal struct {
    Name string
}
 
func (a Animal) Eat() {
    fmt.Printf("%s is eating\n", a.Name)
}
 
type Dog struct {
    Animal
    Breed string
}
 
dog := Dog{
    Animal: Animal{Name: "Buddy"},
    Breed:  "Golden Retriever",
}
dog.Eat()

接口嵌套

type Reader interface {
    Read() string
}
 
type Writer interface {
    Write(string) error
}
 
type ReadWriter interface {
    Reader
    Writer
}

类型断言

var i interface{} = "hello"
 
if s, ok := i.(string); ok {
    fmt.Println("String:", s)
}
 
switch v := i.(type) {
case string:
    fmt.Println("String:", v)
case int:
    fmt.Println("Int:", v)
default:
    fmt.Println("Unknown type")
}

3.2 常见面试题

Q1: Go的接口是如何实现的?

解题思路

  1. 解释Go接口的duck typing特性
  2. 说明接口的底层实现(eface和iface)
  3. 演示接口的定义和实现
  4. 对比Go接口与其他语言的接口

代码实现

package main
 
import "fmt"
 
type Shape interface {
    Area() float64
    Perimeter() float64
}
 
type Rectangle struct {
    Width  float64
    Height float64
}
 
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}
 
func (r Rectangle) Perimeter() float64 {
    return 2 * (r.Width + r.Height)
}
 
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
}
 
func printShapeInfo(s Shape) {
    fmt.Printf("Area: %.2f, Perimeter: %.2f\n", s.Area(), s.Perimeter())
}
 
func main() {
    rect := Rectangle{Width: 5, Height: 3}
    circle := Circle{Radius: 4}
    
    printShapeInfo(rect)
    printShapeInfo(circle)
    
    shapes := []Shape{rect, circle}
    for _, shape := range shapes {
        printShapeInfo(shape)
    }
}

底层实现详解

Go接口的底层实现分为两种:eface(空接口)和iface(非空接口)。

1. eface:空接口的实现

空接口 interface{} 就像一个”万能包装盒”,可以存储任意类型的数据。

// 空接口的底层结构(简化版)
type eface struct {
    _type *类型信息        // 标签:里面装的是什么类型?
    data  unsafe.Pointer   // 数据指针:数据在哪里?
}
 
// 实际例子
var i interface{} = 42
 
// 底层结构:
// eface {
//     _type: &int的类型信息,    // 告诉系统:这是个int
//     data:  &42,              // 指向实际数据42
// }
 
i = "hello"
 
// 底层变成:
// eface {
//     _type: &string的类型信息,  // 告诉系统:这是个string
//     data:  &"hello",          // 指向实际数据"hello"
// }

通俗理解

  • _type:就像快递单上的”物品类型”标签
  • data:就像快递单上的”物品位置”地址

2. iface:非空接口的实现

非空接口(带方法的接口)就像一个”带说明书的包装盒”,多了一个”使用指南”。

// 非空接口的底层结构(简化版)
type iface struct {
    tab  *itab           // 方法表:这个类型有哪些方法?
    data unsafe.Pointer  // 数据指针:数据在哪里?
}
 
// 方法表结构
type itab struct {
    inter  *interfacetype  // 接口类型信息
    _type  *_type          // 具体类型信息
    fun    [1]uintptr      // 方法地址数组(变长)
}
 
// 实际例子
type Writer interface {
    Write([]byte) (int, error)
}
 
type MyWriter struct{}
 
func (w MyWriter) Write(p []byte) (int, error) {
    return len(p), nil
}
 
var w Writer = MyWriter{}
 
// 底层结构:
// iface {
//     tab: &itab {
//         inter:  &Writer_type,    // 接口类型信息
//         _type:  &MyWriter_type,  // 具体类型信息
//         fun:    [1]uintptr{      // 方法地址
//             MyWriter.Write,      // Write方法的地址
//         },
//     },
//     data: &MyWriter{}           // 具体数据
// }

通俗理解

  • tab:就像产品的”使用说明书”,告诉你这个产品有哪些功能
  • data:就像产品本身

3. 方法调用过程

type Writer interface {
    Write([]byte) (int, error)
}
 
type MyWriter struct{}
 
func (w MyWriter) Write(p []byte) (int, error) {
    return len(p), nil
}
 
func main() {
    var w Writer = MyWriter{}
    
    // 这行代码:
    w.Write([]byte("hello"))
    
    // 编译器会把它转换成:
    // 1. 从 w.tab 找到 Write 方法的地址
    // 2. 调用 w.tab.fun[0](w.data, []byte("hello"))
}

通俗理解: 就像你买了一个新产品:

  1. 先看”使用说明书”(tab),找到”如何使用”这一页
  2. 按照说明书操作产品(data)

4. 内存布局对比

// 空接口
var i interface{} = 42
 
// 内存布局(16字节):
// ┌─────────────────────────────────┐
// │ eface                          │
// ├─────────────────────────────────┤
// │ _type: &int_type (8字节)       │ ← 类型信息
// │ data:  &42 (8字节)             │ ← 数据指针
// └─────────────────────────────────┘
//                                   ↓
// ┌─────────────────────────────────┐
// │ 实际数据: 42 (8字节)            │
// └─────────────────────────────────┘
 
// 非空接口
type Writer interface {
    Write([]byte) (int, error)
}
var w Writer = MyWriter{}
 
// 内存布局(16字节):
// ┌─────────────────────────────────┐
// │ iface                          │
// ├─────────────────────────────────┤
// │ tab:  &itab (8字节)            │ ← 方法表指针
// │ data: &MyWriter{} (8字节)      │ ← 数据指针
// └─────────────────────────────────┘
//                                   ↓
// ┌─────────────────────────────────┐
// │ itab (方法表)                   │
// ├─────────────────────────────────┤
// │ 接口类型: Writer                │
// │ 具体类型: MyWriter              │
// │ Write方法: 0x12345678          │ ← 方法地址
// └─────────────────────────────────┘
//                                   ↓
// ┌─────────────────────────────────┐
// │ 实际数据: MyWriter{}            │
// └─────────────────────────────────┘

5. 为什么需要两种接口?

// 场景1:只需要存储数据,不需要调用方法
func printAny(i interface{}) {
    fmt.Println(i)  // 只需要知道类型和值,不需要方法
}
// 用 eface 就够了,结构更简单
 
// 场景2:需要调用方法
func writeData(w Writer) {
    w.Write([]byte("hello"))  // 需要知道Write方法在哪里
}
// 必须用 iface,因为需要方法表来查找方法

通俗类比

  • eface:就像一个普通快递箱,只需要知道里面是什么、在哪里
  • iface:就像一个带说明书的产品包装盒,不仅要知道是什么,还要知道怎么用

6. 性能优势

Go的接口实现具有”零成本抽象”的特点:

  • 编译时确定:方法地址在编译时就确定好了,存储在itab中
  • 直接调用:运行时直接通过方法表调用,不需要查找
  • 无虚函数表开销:不像C++的虚函数需要查表,Go接口调用几乎和直接调用一样快
// C++虚函数调用:需要查虚函数表,有额外开销
// Java接口调用:需要查接口表,有额外开销
// Go接口调用:直接通过itab调用,几乎没有额外开销

常见错误分析

  • 错误1:混淆接口定义和实现
  • 错误2:不理解接口的隐式实现
  • 错误3:误以为接口需要显式声明实现
  • 错误4:不理解eface和iface的区别和使用场景

Q2: Go的值接收者和指针接收者有什么区别?

解题思路

  1. 解释值接收者和指针接收者的区别
  2. 说明各自的适用场景
  3. 演示对结构体状态的影响
  4. 解释接口实现时的注意事项

代码实现

package main
 
import "fmt"
 
type Counter struct {
    count int
}
 
func (c Counter) ValueIncrement() {
    c.count++
    fmt.Printf("ValueIncrement: count = %d\n", c.count)
}
 
func (c *Counter) PointerIncrement() {
    c.count++
    fmt.Printf("PointerIncrement: count = %d\n", c.count)
}
 
func (c Counter) ValueGetCount() int {
    return c.count
}
 
func (c *Counter) PointerGetCount() int {
    return c.count
}
 
type CounterInterface interface {
    Increment()
    GetCount() int
}
 
func main() {
    counter := Counter{count: 0}
    
    counter.ValueIncrement()
    fmt.Printf("After ValueIncrement: count = %d\n", counter.count)
    
    counter.PointerIncrement()
    fmt.Printf("After PointerIncrement: count = %d\n", counter.count)
    
    var ci CounterInterface = &counter
    ci.Increment()
    fmt.Printf("After interface call: count = %d\n", counter.count)
}

接口实现时的注意事项

1. 值接收者 vs 指针接收者的接口实现规则

package main
 
import "fmt"
 
type Writer interface {
    Write([]byte) (int, error)
}
 
type MyWriter struct {
    name string
}
 
// 值接收者方法
func (w MyWriter) WriteValue(p []byte) (int, error) {
    fmt.Printf("值接收者: %s 写入 %s\n", w.name, string(p))
    return len(p), nil
}
 
// 指针接收者方法
func (w *MyWriter) WritePointer(p []byte) (int, error) {
    fmt.Printf("指针接收者: %s 写入 %s\n", w.name, string(p))
    return len(p), nil
}
 
func main() {
    // 情况1:值类型实现值接收者方法
    var w1 MyWriter = MyWriter{name: "writer1"}
    w1.WriteValue([]byte("hello"))  // ✅ 可以调用
    
    // 情况2:指针类型实现值接收者方法
    var w2 *MyWriter = &MyWriter{name: "writer2"}
    w2.WriteValue([]byte("hello"))  // ✅ 可以调用(Go自动解引用)
    
    // 情况3:值类型实现指针接收者方法
    var w3 MyWriter = MyWriter{name: "writer3"}
    // w3.WritePointer([]byte("hello"))  // ❌ 编译错误:不能直接调用
    
    // 情况4:指针类型实现指针接收者方法
    var w4 *MyWriter = &MyWriter{name: "writer4"}
    w4.WritePointer([]byte("hello"))  // ✅ 可以调用
}

核心规则

  • 值接收者方法:值类型和指针类型都可以调用
  • 指针接收者方法:只有指针类型可以调用

2. 接口实现的兼容性

package main
 
import "fmt"
 
type Writer interface {
    Write([]byte) (int, error)
}
 
// 情况1:用值接收者实现接口
type ValueWriter struct {
    name string
}
 
func (w ValueWriter) Write(p []byte) (int, error) {
    fmt.Printf("ValueWriter: %s\n", string(p))
    return len(p), nil
}
 
// 情况2:用指针接收者实现接口
type PointerWriter struct {
    name string
}
 
func (w *PointerWriter) Write(p []byte) (int, error) {
    fmt.Printf("PointerWriter: %s\n", string(p))
    return len(p), nil
}
 
func main() {
    // 值接收者实现的接口
    var vw ValueWriter = ValueWriter{name: "vw"}
    
    // ✅ 值类型可以赋值给接口
    var w1 Writer = vw
    w1.Write([]byte("hello"))
    
    // ✅ 指针类型也可以赋值给接口(Go自动取地址)
    var w2 Writer = &vw
    w2.Write([]byte("world"))
    
    // 指针接收者实现的接口
    var pw *PointerWriter = &PointerWriter{name: "pw"}
    
    // ✅ 指针类型可以赋值给接口
    var w3 Writer = pw
    w3.Write([]byte("hello"))
    
    // ❌ 值类型不能赋值给接口(编译错误)
    // var pw2 PointerWriter = PointerWriter{name: "pw2"}
    // var w4 Writer = pw2  // 编译错误:PointerWriter没有实现Writer接口
}

核心规则

  • 值接收者实现接口:值类型和指针类型都可以赋值给接口
  • 指针接收者实现接口:只有指针类型可以赋值给接口

3. 混合使用值接收者和指针接收者

package main
 
import "fmt"
 
type Counter interface {
    Increment()
    GetCount() int
}
 
type MyCounter struct {
    count int
}
 
// 值接收者方法
func (c MyCounter) GetCount() int {
    return c.count
}
 
// 指针接收者方法
func (c *MyCounter) Increment() {
    c.count++
}
 
func main() {
    // 情况1:使用指针类型
    var c1 *MyCounter = &MyCounter{count: 0}
    
    // ✅ 指针类型可以调用所有方法
    c1.Increment()
    fmt.Printf("count = %d\n", c1.GetCount())
    
    // ✅ 指针类型可以赋值给接口
    var ci1 Counter = c1
    ci1.Increment()
    fmt.Printf("count = %d\n", ci1.GetCount())
    
    // 情况2:使用值类型
    var c2 MyCounter = MyCounter{count: 0}
    
    // ✅ 值类型可以调用值接收者方法
    fmt.Printf("count = %d\n", c2.GetCount())
    
    // ❌ 值类型不能直接调用指针接收者方法
    // c2.Increment()  // 编译错误
    
    // ✅ 值类型可以赋值给接口(但只能调用值接收者方法)
    var ci2 Counter = c2
    ci2.GetCount()  // ✅ 可以调用
    
    // ❌ 但不能调用指针接收者方法
    // ci2.Increment()  // 运行时panic!
}

核心规则

  • 混合使用时,接口只能调用值接收者方法
  • 指针接收者方法需要通过指针类型调用

4. nil指针的调用行为

package main
 
import "fmt"
 
type Writer interface {
    Write([]byte) (int, error)
}
 
type MyWriter struct {
    name string
}
 
// 值接收者方法
func (w MyWriter) WriteValue(p []byte) (int, error) {
    fmt.Printf("值接收者: %s\n", string(p))
    return len(p), nil
}
 
// 指针接收者方法
func (w *MyWriter) WritePointer(p []byte) (int, error) {
    fmt.Printf("指针接收者: %s\n", string(p))
    return len(p), nil
}
 
func main() {
    // 情况1:nil值类型
    var w1 *MyWriter = nil
    
    // ❌ 调用值接收者方法会panic
    // w1.WriteValue([]byte("hello"))  // panic: value method called using nil pointer
    
    // ❌ 调用指针接收者方法也会panic
    // w1.WritePointer([]byte("hello"))  // panic: value method called using nil pointer
    
    // 情况2:nil接口
    var w2 Writer = nil
    
    // ❌ 调用方法会panic
    // w2.Write([]byte("hello"))  // panic: nil pointer dereference
    
    // 情况3:接口存储了nil指针
    var w3 Writer = (*MyWriter)(nil)
    
    // ❌ 调用方法会panic
    // w3.Write([]byte("hello"))  // panic: nil pointer dereference
    
    // 情况4:安全的nil检查
    var w4 *MyWriter = nil
    if w4 != nil {
        w4.WriteValue([]byte("hello"))
    } else {
        fmt.Println("w4是nil,不能调用方法")
    }
}

核心规则

  • nil指针不能调用任何方法(值接收者或指针接收者)
  • nil接口不能调用方法
  • 调用前必须进行nil检查·

5. 方法集(Method Set)规则

package main
 
import "fmt"
 
type Writer interface {
    Write([]byte) (int, error)
}
 
type MyWriter struct {
    name string
}
 
// 值接收者方法
func (w MyWriter) WriteValue(p []byte) (int, error) {
    return len(p), nil
}
 
// 指针接收者方法
func (w *MyWriter) WritePointer(p []byte) (int, error) {
    return len(p), nil
}
 
func main() {
    // 值类型的方法集
    var v MyWriter
    fmt.Printf("值类型的方法集:\n")
    fmt.Printf("- WriteValue: %v\n", hasMethod(v, "WriteValue"))
    fmt.Printf("- WritePointer: %v\n", hasMethod(v, "WritePointer"))
    
    // 指针类型的方法集
    var p *MyWriter
    fmt.Printf("\n指针类型的方法集:\n")
    fmt.Printf("- WriteValue: %v\n", hasMethod(p, "WriteValue"))
    fmt.Printf("- WritePointer: %v\n", hasMethod(p, "WritePointer"))
}
 
func hasMethod(i interface{}, methodName string) bool {
    // 简化的方法检查
    switch i.(type) {
    case MyWriter:
        return methodName == "WriteValue"
    case *MyWriter:
        return methodName == "WriteValue" || methodName == "WritePointer"
    }
    return false
}

核心规则

  • 值类型的方法集:只包含值接收者方法
  • 指针类型的方法集:包含值接收者方法和指针接收者方法

6. 实际应用场景

package main
 
import "fmt"
 
type Writer interface {
    Write([]byte) (int, error)
}
 
// 场景1:只读操作,使用值接收者
type FileReader struct {
    path string
}
 
func (r FileReader) Read() ([]byte, error) {
    fmt.Printf("读取文件: %s\n", r.path)
    return []byte("data"), nil
}
 
// 场景2:需要修改状态,使用指针接收者
type FileWriter struct {
    path  string
    count int
}
 
func (w *FileWriter) Write(data []byte) (int, error) {
    w.count++
    fmt.Printf("写入文件: %s (第%d次)\n", w.path, w.count)
    return len(data), nil
}
 
func (w *FileWriter) GetCount() int {
    return w.count
}
 
func main() {
    // 只读操作:值类型
    var reader FileReader = FileReader{path: "test.txt"}
    data, _ := reader.Read()
    fmt.Printf("读取数据: %s\n", string(data))
    
    // 可写操作:指针类型
    var writer *FileWriter = &FileWriter{path: "output.txt"}
    writer.Write([]byte("data1"))
    writer.Write([]byte("data2"))
    fmt.Printf("写入次数: %d\n", writer.GetCount())
}

最佳实践

  • 只读操作:使用值接收者
  • 需要修改状态:使用指针接收者
  • 大型结构体:使用指针接收者(避免拷贝)
  • 小型结构体:可以使用值接收者

7. 接口类型断言的注意事项

package main
 
import "fmt"
 
type Writer interface {
    Write([]byte) (int, error)
}
 
type MyWriter struct {
    name string
}
 
func (w *MyWriter) Write(p []byte) (int, error) {
    fmt.Printf("%s 写入: %s\n", w.name, string(p))
    return len(p), nil
}
 
func main() {
    // 情况1:接口存储指针类型
    var w1 Writer = &MyWriter{name: "writer1"}
    
    // ✅ 断言为指针类型
    if pw, ok := w1.(*MyWriter); ok {
        pw.Write([]byte("hello"))
    }
    
    // ❌ 断言为值类型(编译警告,运行时失败)
    // if vw, ok := w1.(MyWriter); ok {
    //     vw.Write([]byte("hello"))  // 编译警告:MyWriter没有实现Writer接口
    // }
    
    // 情况2:类型switch
    switch v := w1.(type) {
    case *MyWriter:
        v.Write([]byte("world"))
    default:
        fmt.Println("未知类型")
    }
}

核心规则

  • 接口存储的类型必须与断言的类型匹配
  • 指针接收者实现的接口只能断言为指针类型

8. 性能考虑

package main
 
import "testing"
 
type Writer interface {
    Write([]byte) (int, error)
}
 
type SmallWriter struct {
    data [8]byte
}
 
type LargeWriter struct {
    data [1024]byte
}
 
// 值接收者
func (w SmallWriter) WriteValue(p []byte) (int, error) {
    return len(p), nil
}
 
// 指针接收者
func (w *SmallWriter) WritePointer(p []byte) (int, error) {
    return len(p), nil
}
 
// 小结构体:值接收者更快
func BenchmarkSmallValueReceiver(b *testing.B) {
    var w SmallWriter = SmallWriter{}
    for i := 0; i < b.N; i++ {
        w.WriteValue([]byte("data"))
    }
}
 
func BenchmarkSmallPointerReceiver(b *testing.B) {
    var w *SmallWriter = &SmallWriter{}
    for i := 0; i < b.N; i++ {
        w.WritePointer([]byte("data"))
    }
}
 
// 大结构体:指针接收者更快
func BenchmarkLargeValueReceiver(b *testing.B) {
    var w LargeWriter = LargeWriter{}
    for i := 0; i < b.N; i++ {
        w.WriteValue([]byte("data"))
    }
}
 
func BenchmarkLargePointerReceiver(b *testing.B) {
    var w *LargeWriter = &LargeWriter{}
    for i := 0; i < b.N; i++ {
        w.WritePointer([]byte("data"))
    }
}

性能对比

  • 小结构体(< 64字节):值接收者更快(避免解引用)
  • 大结构体(> 64字节):指针接收者更快(避免拷贝)

常见错误分析

  • 错误1:在值接收者方法中修改结构体状态
  • 错误2:混淆值类型和指针类型的接口实现
  • 错误3:不理解nil指针的调用行为
  • 错误4:混合使用值接收者和指针接收者时,接口只能调用值接收者方法
  • 错误5:大型结构体使用值接收者导致性能问题

Q3: Go的组合是如何替代继承的?

解题思路

  1. 解释Go不支持继承的设计理念
  2. 演示组合的使用方式
  3. 说明组合的优势(灵活性、解耦)
  4. 对比组合和继承的区别

代码实现

package main
 
import "fmt"
 
type Engine struct {
    Power int
}
 
func (e Engine) Start() {
    fmt.Printf("Engine started with %d HP\n", e.Power)
}
 
type Vehicle struct {
    Engine
    Name string
}
 
func (v Vehicle) Drive() {
    fmt.Printf("%s is driving\n", v.Name)
}
 
type Car struct {
    Vehicle
    Doors int
}
 
type Motorcycle struct {
    Vehicle
    HasSidecar bool
}
 
func main() {
    car := Car{
        Vehicle: Vehicle{
            Engine: Engine{Power: 200},
            Name:   "Sedan",
        },
        Doors: 4,
    }
    
    car.Start()
    car.Drive()
    fmt.Printf("Car has %d doors\n", car.Doors)
    
    moto := Motorcycle{
        Vehicle: Vehicle{
            Engine: Engine{Power: 100},
            Name:   "Sport Bike",
        },
        HasSidecar: false,
    }
    
    moto.Start()
    moto.Drive()
    fmt.Printf("Motorcycle has sidecar: %v\n", moto.HasSidecar)
}

Go组合的详细说明

1. Go不支持继承的设计理念

Go语言设计者有意避免了传统的继承机制,原因如下:

// ❌ Go不支持这样的继承
class Animal {
    func speak() { println("animal sound") }
}
 
class Dog extends Animal {
    func speak() { println("bark") }
}
 
// ✅ Go使用组合替代继承
type Animal struct {
    Name string
}
 
func (a Animal) Speak() {
    fmt.Printf("%s makes a sound\n", a.Name)
}
 
type Dog struct {
    Animal  // 组合:嵌入Animal
    Breed   string
}
 
func (d Dog) Bark() {
    fmt.Printf("%s barks\n", d.Name)
}

设计理念

  • 组合优于继承:组合提供更大的灵活性
  • 显式优于隐式:明确表达依赖关系
  • 避免继承地狱:防止深层继承层次
  • 接口解耦:通过接口实现多态

2. 组合的使用方式

2.1 基本组合
package main
 
import "fmt"
 
// 基础组件:引擎
type Engine struct {
    Power    int
    FuelType string
}
 
func (e Engine) Start() {
    fmt.Printf("Engine started: %d HP, %s fuel\n", e.Power, e.FuelType)
}
 
func (e Engine) Stop() {
    fmt.Println("Engine stopped")
}
 
// 基础组件:车轮
type Wheels struct {
    Count int
    Size  int
}
 
func (w Wheels) Rotate() {
    fmt.Printf("%d wheels rotating\n", w.Count)
}
 
// 组合:车辆
type Vehicle struct {
    Engine  // 嵌入引擎
    Wheels  // 嵌入车轮
    Name    string
}
 
func (v Vehicle) Drive() {
    fmt.Printf("%s is driving\n", v.Name)
    v.Engine.Start()
    v.Wheels.Rotate()
}
 
func main() {
    car := Vehicle{
        Engine: Engine{Power: 200, FuelType: "Gasoline"},
        Wheels: Wheels{Count: 4, Size: 18},
        Name:   "Sedan",
    }
    
    car.Drive()
    car.Engine.Stop()
}
2.2 多层组合
package main
 
import "fmt"
 
// 第一层:基础组件
type Engine struct {
    Power int
}
 
func (e Engine) Start() {
    fmt.Printf("Engine started: %d HP\n", e.Power)
}
 
type Wheels struct {
    Count int
}
 
func (w Wheels) Rotate() {
    fmt.Printf("%d wheels rotating\n", w.Count)
}
 
// 第二层:车辆
type Vehicle struct {
    Engine
    Wheels
    Name string
}
 
func (v Vehicle) Drive() {
    fmt.Printf("%s is driving\n", v.Name)
}
 
// 第三层:汽车
type Car struct {
    Vehicle
    Doors int
}
 
func (c Car) OpenDoor() {
    fmt.Printf("Opening %d doors\n", c.Doors)
}
 
// 第三层:摩托车
type Motorcycle struct {
    Vehicle
    HasSidecar bool
}
 
func (m Motorcycle) Lean() {
    fmt.Println("Motorcycle leaning in turn")
}
 
func main() {
    // 汽车:三层组合
    car := Car{
        Vehicle: Vehicle{
            Engine: Engine{Power: 200},
            Wheels: Wheels{Count: 4},
            Name:   "Sedan",
        },
        Doors: 4,
    }
    
    car.Start()      // 来自Engine
    car.Drive()      // 来自Vehicle
    car.OpenDoor()   // 来自Car
    
    // 摩托车:三层组合
    moto := Motorcycle{
        Vehicle: Vehicle{
            Engine: Engine{Power: 100},
            Wheels: Wheels{Count: 2},
            Name:   "Sport Bike",
        },
        HasSidecar: false,
    }
    
    moto.Start()    // 来自Engine
    moto.Drive()    // 来自Vehicle
    moto.Lean()     // 来自Motorcycle
}
2.3 方法覆盖
package main
 
import "fmt"
 
// 基础组件
type Engine struct {
    Power int
}
 
func (e Engine) Start() {
    fmt.Printf("Engine started: %d HP\n", e.Power)
}
 
// 车辆
type Vehicle struct {
    Engine
    Name string
}
 
// 覆盖Engine的Start方法
func (v Vehicle) Start() {
    fmt.Printf("%s starting...\n", v.Name)
    v.Engine.Start()  // 调用被覆盖的方法
}
 
func main() {
    vehicle := Vehicle{
        Engine: Engine{Power: 200},
        Name:   "Sedan",
    }
    
    // 调用Vehicle的Start方法(覆盖了Engine的Start)
    vehicle.Start()
}
2.4 命名冲突处理
package main
 
import "fmt"
 
// 两个组件都有相同的方法
type ComponentA struct {
    Name string
}
 
func (a ComponentA) Process() {
    fmt.Printf("ComponentA processing: %s\n", a.Name)
}
 
type ComponentB struct {
    Name string
}
 
func (b ComponentB) Process() {
    fmt.Printf("ComponentB processing: %s\n", b.Name)
}
 
// 组合两个有冲突的组件
type Composite struct {
    ComponentA
    ComponentB
}
 
func main() {
    comp := Composite{
        ComponentA: ComponentA{Name: "A"},
        ComponentB: ComponentB{Name: "B"},
    }
    
    // ❌ 编译错误:有歧义
    // comp.Process()  // ambiguous selector comp.Process
    
    // ✅ 明确指定调用哪个组件的方法
    comp.ComponentA.Process()  // 调用ComponentA的Process
    comp.ComponentB.Process()  // 调用ComponentB的Process
    
    // 字段冲突
    fmt.Println(comp.ComponentA.Name)  // A
    fmt.Println(comp.ComponentB.Name)  // B
}

3. 组合的优势

3.1 灵活性
package main
 
import "fmt"
 
// 组件:引擎
type Engine struct {
    Power int
}
 
func (e Engine) Start() {
    fmt.Printf("Engine started: %d HP\n", e.Power)
}
 
// 组件:电池(电动车)
type Battery struct {
    Capacity int
}
 
func (b Battery) Start() {
    fmt.Printf("Battery started: %d kWh\n", b.Capacity)
}
 
// 组件:太阳能(太阳能车)
type SolarPanel struct {
    Efficiency float64
}
 
func (s SolarPanel) Start() {
    fmt.Printf("Solar panel started: %.1f%% efficiency\n", s.Efficiency*100)
}
 
// 燃油车
type GasCar struct {
    Engine
    Name string
}
 
// 电动车
type ElectricCar struct {
    Battery
    Name string
}
 
// 太阳能车
type SolarCar struct {
    SolarPanel
    Name string
}
 
func main() {
    // 燃油车
    gasCar := GasCar{
        Engine: Engine{Power: 200},
        Name:   "Gas Car",
    }
    gasCar.Start()
    
    // 电动车
    electricCar := ElectricCar{
        Battery: Battery{Capacity: 75},
        Name:    "Electric Car",
    }
    electricCar.Start()
    
    // 太阳能车
    solarCar := SolarCar{
        SolarPanel: SolarPanel{Efficiency: 0.22},
        Name:       "Solar Car",
    }
    solarCar.Start()
}

优势:可以灵活组合不同的组件,创建不同类型的对象。

3.2 解耦
package main
 
import "fmt"
 
// 独立的组件:日志
type Logger struct {
    Prefix string
}
 
func (l Logger) Log(message string) {
    fmt.Printf("[%s] %s\n", l.Prefix, message)
}
 
// 独立的组件:配置
type Config struct {
    Debug bool
}
 
func (c Config) IsDebug() bool {
    return c.Debug
}
 
// 独立的组件:数据库
type Database struct {
    Host string
}
 
func (d Database) Connect() {
    fmt.Printf("Connecting to %s\n", d.Host)
}
 
// 服务:组合多个独立组件
type Service struct {
    Logger
    Config
    Database
    Name string
}
 
func (s Service) Run() {
    if s.IsDebug() {
        s.Log("Debug mode enabled")
    }
    s.Connect()
    s.Log("Service started")
}
 
func main() {
    service := Service{
        Logger:   Logger{Prefix: "SERVICE"},
        Config:   Config{Debug: true},
        Database: Database{Host: "localhost"},
        Name:     "MyService",
    }
    
    service.Run()
}

优势:每个组件都是独立的,可以单独测试和复用。

3.3 接口与组合结合
package main
 
import "fmt"
 
// 接口:定义行为
type Writer interface {
    Write([]byte) (int, error)
}
 
type Reader interface {
    Read([]byte) (int, error)
}
 
// 组件:文件
type File struct {
    name string
}
 
func (f File) Write(p []byte) (int, error) {
    fmt.Printf("File %s writing: %s\n", f.name, string(p))
    return len(p), nil
}
 
func (f File) Read(p []byte) (int, error) {
    fmt.Printf("File %s reading\n", f.name)
    return len(p), nil
}
 
// 组件:网络
type Network struct {
    address string
}
 
func (n Network) Write(p []byte) (int, error) {
    fmt.Printf("Network %s sending: %s\n", n.address, string(p))
    return len(p), nil
}
 
func (n Network) Read(p []byte) (int, error) {
    fmt.Printf("Network %s receiving\n", n.address)
    return len(p), nil
}
 
// 组合:文件系统
type FileSystem struct {
    File
}
 
func (fs FileSystem) Save(data []byte) error {
    fs.Write(data)
    return nil
}
 
// 组合:网络系统
type NetworkSystem struct {
    Network
}
 
func (ns NetworkSystem) Send(data []byte) error {
    ns.Write(data)
    return nil
}
 
// 组合:混合系统
type HybridSystem struct {
    File
    Network
}
 
func (hs HybridSystem) Sync(data []byte) error {
    hs.File.Write(data)
    hs.Network.Write(data)
    return nil
}
 
func main() {
    // 文件系统
    fs := FileSystem{File: File{name: "data.txt"}}
    fs.Save([]byte("hello"))
    
    // 网络系统
    ns := NetworkSystem{Network: Network{address: "192.168.1.1"}}
    ns.Send([]byte("hello"))
    
    // 混合系统
    hs := HybridSystem{
        File:    File{name: "data.txt"},
        Network: Network{address: "192.168.1.1"},
    }
    hs.Sync([]byte("hello"))
}

优势:接口定义行为,组合实现功能,两者结合提供最大的灵活性。

4. 对比组合和继承

4.1 继承的问题
// ❌ 继承的问题(伪代码)
 
// 问题1:脆弱基类
class Animal {
    func speak() { println("animal sound") }
}
 
class Dog extends Animal {
    func speak() { println("bark") }
}
 
class Cat extends Animal {
    func speak() { println("meow") }
}
 
// 如果Animal的speak方法改变了,所有子类都会受影响
 
// 问题2:菱形继承
class A {
    func method() { println("A") }
}
 
class B extends A {}
class C extends A {}
 
class D extends B, C {}  // 哪个method()?
 
// 问题3:紧耦合
class Bird extends Animal {
    func fly() { println("flying") }
}
 
class Penguin extends Bird {
    // 企鹅不会飞,但继承了fly方法
}
 
// 问题4:深层继承难以理解
class A extends B {}
class B extends C {}
class C extends D {}
class D extends E {}
class E extends F {}
// 修改A可能影响B、C、D、E、F
4.2 组合的优势
// ✅ 组合的优势
 
// 优势1:独立组件
type Flyable struct{}
 
func (f Flyable) Fly() {
    fmt.Println("flying")
}
 
type Swimmable struct{}
 
func (s Swimmable) Swim() {
    fmt.Println("swimming")
}
 
type Bird struct {
    Flyable
    Name string
}
 
type Penguin struct {
    Swimmable
    Name string
}
 
func main() {
    bird := Bird{Flyable: Flyable{}, Name: "Eagle"}
    bird.Fly()
    
    penguin := Penguin{Swimmable: Swimmable{}, Name: "Penguin"}
    penguin.Swim()
    // penguin.Fly()  // 编译错误:Penguin没有Fly方法
}
 
// 优势2:灵活组合
type FlyingFish struct {
    Flyable
    Swimmable
    Name string
}
 
func main() {
    fish := FlyingFish{
        Flyable:   Flyable{},
        Swimmable: Swimmable{},
        Name:      "Flying Fish",
    }
    fish.Fly()
    fish.Swim()
}
 
// 优势3:浅层结构
type Engine struct {
    Power int
}
 
type Wheels struct {
    Count int
}
 
type Car struct {
    Engine
    Wheels
    Name string
}
 
// 结构清晰,易于理解
 
// 优势4:松耦合
type Logger struct {
    Prefix string
}
 
func (l Logger) Log(message string) {
    fmt.Printf("[%s] %s\n", l.Prefix, message)
}
 
type Service struct {
    Logger
    Name string
}
 
// Logger可以独立修改,不影响Service
4.3 对比表格
特性继承组合
耦合度高(紧耦合)低(松耦合)
灵活性低(编译时确定)高(运行时组合)
复用性受限(单继承)灵活(多重组合)
复杂性高(深层继承)低(浅层结构)
可测试性难(依赖父类)易(组件独立)
可维护性差(修改影响大)好(局部修改)
命名冲突复杂(方法覆盖)明确(显式调用)

5. 实际应用场景

5.1 Web框架中的组合
package main
 
import "fmt"
 
// 基础组件:路由
type Router struct {
    routes map[string]func()
}
 
func (r *Router) AddRoute(path string, handler func()) {
    r.routes[path] = handler
}
 
func (r *Router) Handle(path string) {
    if handler, ok := r.routes[path]; ok {
        handler()
    }
}
 
// 基础组件:中间件
type Middleware struct {
    handlers []func(func())
}
 
func (m *Middleware) Use(handler func(func())) {
    m.handlers = append(m.handlers, handler)
}
 
func (m *Middleware) Run(handler func()) {
    for _, h := range m.handlers {
        handler = h(handler)
    }
    handler()
}
 
// 组合:HTTP服务器
type HTTPServer struct {
    Router
    Middleware
    Port int
}
 
func (s *HTTPServer) Start() {
    fmt.Printf("Server started on port %d\n", s.Port)
    s.Handle("/")
}
 
func main() {
    server := HTTPServer{
        Router: Router{routes: make(map[string]func())},
        Middleware: Middleware{},
        Port: 8080,
    }
    
    server.AddRoute("/", func() {
        fmt.Println("Home page")
    })
    
    server.Use(func(next func()) {
        fmt.Println("Middleware 1")
        next()
    })
    
    server.Start()
}
5.2 游戏开发中的组合
package main
 
import "fmt"
 
// 组件:移动
type Movable struct {
    Speed float64
}
 
func (m Movable) Move() {
    fmt.Printf("Moving at speed %.1f\n", m.Speed)
}
 
// 组件:攻击
type Attackable struct {
    Damage int
}
 
func (a Attackable) Attack() {
    fmt.Printf("Attacking with damage %d\n", a.Damage)
}
 
// 组件:防御
type Defendable struct {
    Armor int
}
 
func (d Defendable) Defend() {
    fmt.Printf("Defending with armor %d\n", d.Armor)
}
 
// 组合:战士
type Warrior struct {
    Movable
    Attackable
    Defendable
    Name string
}
 
// 组合:弓箭手
type Archer struct {
    Movable
    Attackable
    Name string
}
 
func main() {
    // 战士:可以移动、攻击、防御
    warrior := Warrior{
        Movable:    Movable{Speed: 5.0},
        Attackable: Attackable{Damage: 50},
        Defendable: Defendable{Armor: 30},
        Name:       "Warrior",
    }
    
    fmt.Printf("%s:\n", warrior.Name)
    warrior.Move()
    warrior.Attack()
    warrior.Defend()
    
    // 弓箭手:可以移动、攻击,但不能防御
    archer := Archer{
        Movable:    Movable{Speed: 7.0},
        Attackable: Attackable{Damage: 40},
        Name:       "Archer",
    }
    
    fmt.Printf("\n%s:\n", archer.Name)
    archer.Move()
    archer.Attack()
    // archer.Defend()  // 编译错误:Archer没有Defend方法
}

常见错误分析

  • 错误1:过度嵌套导致代码难以理解
  • 错误2:命名冲突时不知道如何访问
  • 错误3:不理解组合的底层实现
  • 错误4:滥用组合,导致结构过于复杂
  • 错误5:不使用接口,限制了灵活性

Q4: 如何使用类型断言和类型选择?

解题思路

  1. 解释类型断言的语法和用途
  2. 演示安全的类型断言
  3. 说明类型选择(type switch)的使用
  4. 展示实际应用场景

代码实现

package main
 
import "fmt"
 
func processValue(i interface{}) {
    if s, ok := i.(string); ok {
        fmt.Printf("String: %s, length: %d\n", s, len(s))
    } else if n, ok := i.(int); ok {
        fmt.Printf("Int: %d, doubled: %d\n", n, n*2)
    } else {
        fmt.Printf("Unknown type: %T\n", i)
    }
}
 
func describeType(i interface{}) {
    switch v := i.(type) {
    case string:
        fmt.Printf("String: %s\n", v)
    case int:
        fmt.Printf("Int: %d\n", v)
    case bool:
        fmt.Printf("Bool: %v\n", v)
    case []int:
        fmt.Printf("Slice of int: %v\n", v)
    case map[string]int:
        fmt.Printf("Map[string]int: %v\n", v)
    default:
        fmt.Printf("Unknown type: %T\n", v)
    }
}
 
type Speaker interface {
    Speak() string
}
 
type Dog struct {
    Name string
}
 
func (d Dog) Speak() string {
    return "Woof!"
}
 
type Cat struct {
    Name string
}
 
func (c Cat) Speak() string {
    return "Meow!"
}
 
func makeAnimalSpeak(speaker Speaker) {
    if dog, ok := speaker.(Dog); ok {
        fmt.Printf("%s says: %s\n", dog.Name, dog.Speak())
    } else if cat, ok := speaker.(Cat); ok {
        fmt.Printf("%s says: %s\n", cat.Name, cat.Speak())
    }
}
 
func main() {
    processValue("hello")
    processValue(42)
    processValue(3.14)
    
    describeType("hello")
    describeType(42)
    describeType(true)
    describeType([]int{1, 2, 3})
    describeType(map[string]int{"a": 1, "b": 2})
    
    dog := Dog{Name: "Buddy"}
    cat := Cat{Name: "Whiskers"}
    
    makeAnimalSpeak(dog)
    makeAnimalSpeak(cat)
}

常见错误分析

  • 错误1:不安全的类型断言导致panic
  • 错误2:忘记检查断言是否成功
  • 错误3:在类型选择中忘记使用type关键字