Skip to content

Go 语言接口

接口是 Go 语言最重要的特性之一,它定义了对象的行为规范。Go 的接口是隐式实现的,这使得代码更加灵活和可扩展。

📋 接口基础概念

接口定义和实现

go
package main

import "fmt"

// 定义接口
type Speaker interface {
    Speak() string
}

// 定义结构体
type Dog struct {
    Name string
}

type Cat struct {
    Name string
}

type Human struct {
    Name string
    Language string
}

// 实现接口(隐式实现)
func (d Dog) Speak() string {
    return fmt.Sprintf("%s 说:汪汪汪!", d.Name)
}

func (c Cat) Speak() string {
    return fmt.Sprintf("%s 说:喵喵喵!", c.Name)
}

func (h Human) Speak() string {
    return fmt.Sprintf("%s%s说:你好!", h.Name, h.Language)
}

// 使用接口的函数
func makeSound(speaker Speaker) {
    fmt.Println(speaker.Speak())
}

func main() {
    // 创建不同类型的实例
    dog := Dog{Name: "小黄"}
    cat := Cat{Name: "小白"}
    human := Human{Name: "张三", Language: "中文"}
    
    // 所有类型都实现了 Speaker 接口
    fmt.Println("=== 接口多态演示 ===")
    makeSound(dog)
    makeSound(cat)
    makeSound(human)
    
    // 接口切片
    fmt.Println("\n=== 接口切片 ===")
    speakers := []Speaker{dog, cat, human}
    
    for i, speaker := range speakers {
        fmt.Printf("第 %d 个说话者: %s\n", i+1, speaker.Speak())
    }
}

空接口

go
package main

import "fmt"

// 空接口可以接受任何类型
func printValue(value interface{}) {
    fmt.Printf("值: %v, 类型: %T\n", value, value)
}

// 处理任意类型的切片
func printSlice(items []interface{}) {
    for i, item := range items {
        fmt.Printf("索引 %d: %v (%T)\n", i, item, item)
    }
}

func main() {
    fmt.Println("=== 空接口演示 ===")
    
    // 空接口可以接受任何值
    printValue(42)
    printValue("Hello, World!")
    printValue(3.14159)
    printValue(true)
    printValue([]int{1, 2, 3})
    
    // 空接口切片
    fmt.Println("\n=== 混合类型切片 ===")
    mixedSlice := []interface{}{
        "字符串",
        123,
        3.14,
        true,
        []string{"a", "b", "c"},
        map[string]int{"score": 100},
    }
    
    printSlice(mixedSlice)
    
    // 使用类型断言处理空接口
    fmt.Println("\n=== 类型断言处理 ===")
    var unknown interface{} = "这是一个字符串"
    
    if str, ok := unknown.(string); ok {
        fmt.Printf("这是字符串: %s (长度: %d)\n", str, len(str))
    } else {
        fmt.Println("不是字符串类型")
    }
}

🎯 接口组合

接口嵌套

go
package main

import "fmt"

// 基础接口
type Reader interface {
    Read() string
}

type Writer interface {
    Write(data string) error
}

type Closer interface {
    Close() error
}

// 组合接口
type ReadWriter interface {
    Reader
    Writer
}

type ReadWriteCloser interface {
    Reader
    Writer
    Closer
}

// 实现结构体
type File struct {
    name    string
    content string
    isOpen  bool
}

func NewFile(name string) *File {
    return &File{
        name:   name,
        isOpen: true,
    }
}

// 实现所有接口方法
func (f *File) Read() string {
    if !f.isOpen {
        return "文件已关闭"
    }
    return f.content
}

func (f *File) Write(data string) error {
    if !f.isOpen {
        return fmt.Errorf("文件已关闭")
    }
    f.content = data
    return nil
}

func (f *File) Close() error {
    if !f.isOpen {
        return fmt.Errorf("文件已经关闭")
    }
    f.isOpen = false
    fmt.Printf("文件 %s 已关闭\n", f.name)
    return nil
}

// 使用组合接口的函数
func processFile(rwc ReadWriteCloser) {
    // 写入数据
    err := rwc.Write("Hello, Go Interface!")
    if err != nil {
        fmt.Printf("写入错误: %v\n", err)
        return
    }
    
    // 读取数据
    content := rwc.Read()
    fmt.Printf("读取内容: %s\n", content)
    
    // 关闭文件
    err = rwc.Close()
    if err != nil {
        fmt.Printf("关闭错误: %v\n", err)
    }
}

func main() {
    fmt.Println("=== 接口组合演示 ===")
    
    file := NewFile("test.txt")
    
    // File 实现了 ReadWriteCloser 接口
    processFile(file)
    
    // 尝试再次操作已关闭的文件
    fmt.Println("\n=== 操作已关闭文件 ===")
    err := file.Write("尝试写入")
    if err != nil {
        fmt.Printf("写入失败: %v\n", err)
    }
    
    content := file.Read()
    fmt.Printf("读取结果: %s\n", content)
}

接口适配器模式

go
package main

import "fmt"

// 原有接口
type OldPrinter interface {
    PrintOld(text string)
}

// 新接口
type NewPrinter interface {
    Print(text string, color string)
}

// 老式打印机
type LegacyPrinter struct {
    model string
}

func (lp LegacyPrinter) PrintOld(text string) {
    fmt.Printf("[%s] 打印: %s\n", lp.model, text)
}

// 适配器:让老式打印机实现新接口
type PrinterAdapter struct {
    oldPrinter OldPrinter
}

func (pa PrinterAdapter) Print(text string, color string) {
    // 适配逻辑:忽略颜色参数,调用老接口
    coloredText := fmt.Sprintf("%s (%s色)", text, color)
    pa.oldPrinter.PrintOld(coloredText)
}

// 现代打印机
type ModernPrinter struct {
    model string
}

func (mp ModernPrinter) Print(text string, color string) {
    fmt.Printf("[%s] 彩色打印: %s (颜色: %s)\n", mp.model, text, color)
}

// 统一使用新接口的函数
func printDocument(printer NewPrinter, document string) {
    printer.Print(document, "黑色")
}

func main() {
    fmt.Println("=== 适配器模式演示 ===")
    
    // 创建打印机
    legacy := LegacyPrinter{model: "老式-100"}
    modern := ModernPrinter{model: "现代-200"}
    
    // 使用适配器包装老式打印机
    adapter := PrinterAdapter{oldPrinter: legacy}
    
    document := "重要文档内容"
    
    fmt.Println("现代打印机:")
    printDocument(modern, document)
    
    fmt.Println("\n通过适配器使用老式打印机:")
    printDocument(adapter, document)
}

🔧 实际应用示例

数据库接口设计

go
package main

import (
    "fmt"
    "time"
)

// 数据库操作接口
type Database interface {
    Connect() error
    Disconnect() error
    Query(sql string) ([]map[string]interface{}, error)
    Execute(sql string) error
}

// 缓存接口
type Cache interface {
    Get(key string) (interface{}, bool)
    Set(key string, value interface{}, ttl time.Duration)
    Delete(key string)
}

// MySQL 数据库实现
type MySQL struct {
    host     string
    database string
    connected bool
}

func NewMySQL(host, database string) *MySQL {
    return &MySQL{
        host:     host,
        database: database,
    }
}

func (m *MySQL) Connect() error {
    fmt.Printf("连接到 MySQL: %s/%s\n", m.host, m.database)
    m.connected = true
    return nil
}

func (m *MySQL) Disconnect() error {
    fmt.Printf("断开 MySQL 连接\n")
    m.connected = false
    return nil
}

func (m *MySQL) Query(sql string) ([]map[string]interface{}, error) {
    if !m.connected {
        return nil, fmt.Errorf("数据库未连接")
    }
    
    fmt.Printf("MySQL 查询: %s\n", sql)
    // 模拟查询结果
    return []map[string]interface{}{
        {"id": 1, "name": "张三"},
        {"id": 2, "name": "李四"},
    }, nil
}

func (m *MySQL) Execute(sql string) error {
    if !m.connected {
        return fmt.Errorf("数据库未连接")
    }
    
    fmt.Printf("MySQL 执行: %s\n", sql)
    return nil
}

// Redis 缓存实现
type Redis struct {
    host string
    data map[string]interface{}
}

func NewRedis(host string) *Redis {
    return &Redis{
        host: host,
        data: make(map[string]interface{}),
    }
}

func (r *Redis) Get(key string) (interface{}, bool) {
    value, exists := r.data[key]
    fmt.Printf("Redis GET %s: %v (存在: %v)\n", key, value, exists)
    return value, exists
}

func (r *Redis) Set(key string, value interface{}, ttl time.Duration) {
    r.data[key] = value
    fmt.Printf("Redis SET %s = %v (TTL: %v)\n", key, value, ttl)
}

func (r *Redis) Delete(key string) {
    delete(r.data, key)
    fmt.Printf("Redis DEL %s\n", key)
}

// 数据访问层
type DataAccessLayer struct {
    db    Database
    cache Cache
}

func NewDataAccessLayer(db Database, cache Cache) *DataAccessLayer {
    return &DataAccessLayer{
        db:    db,
        cache: cache,
    }
}

func (dal *DataAccessLayer) GetUser(id string) (map[string]interface{}, error) {
    cacheKey := fmt.Sprintf("user:%s", id)
    
    // 首先尝试从缓存获取
    if user, found := dal.cache.Get(cacheKey); found {
        fmt.Println("从缓存返回用户数据")
        return user.(map[string]interface{}), nil
    }
    
    // 缓存未命中,从数据库查询
    fmt.Println("缓存未命中,从数据库查询")
    sql := fmt.Sprintf("SELECT * FROM users WHERE id = %s", id)
    results, err := dal.db.Query(sql)
    if err != nil {
        return nil, err
    }
    
    if len(results) == 0 {
        return nil, fmt.Errorf("用户不存在")
    }
    
    user := results[0]
    
    // 将结果存入缓存
    dal.cache.Set(cacheKey, user, 5*time.Minute)
    
    return user, nil
}

func (dal *DataAccessLayer) CreateUser(name string) error {
    sql := fmt.Sprintf("INSERT INTO users (name) VALUES ('%s')", name)
    return dal.db.Execute(sql)
}

func main() {
    fmt.Println("=== 数据访问层演示 ===")
    
    // 创建数据库和缓存实例
    mysql := NewMySQL("localhost:3306", "testdb")
    redis := NewRedis("localhost:6379")
    
    // 创建数据访问层
    dal := NewDataAccessLayer(mysql, redis)
    
    // 连接数据库
    err := mysql.Connect()
    if err != nil {
        fmt.Printf("连接失败: %v\n", err)
        return
    }
    defer mysql.Disconnect()
    
    // 测试用户操作
    fmt.Println("\n--- 创建用户 ---")
    err = dal.CreateUser("王五")
    if err != nil {
        fmt.Printf("创建用户失败: %v\n", err)
        return
    }
    
    fmt.Println("\n--- 第一次获取用户 ---")
    user, err := dal.GetUser("1")
    if err != nil {
        fmt.Printf("获取用户失败: %v\n", err)
        return
    }
    fmt.Printf("用户信息: %v\n", user)
    
    fmt.Println("\n--- 第二次获取用户(应该从缓存返回)---")
    user, err = dal.GetUser("1")
    if err != nil {
        fmt.Printf("获取用户失败: %v\n", err)
        return
    }
    fmt.Printf("用户信息: %v\n", user)
}

策略模式实现

go
package main

import (
    "fmt"
    "math"
)

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

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

func (bs BubbleSort) Name() string {
    return "冒泡排序"
}

func (bs 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
}

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

func (qs QuickSort) Name() string {
    return "快速排序"
}

func (qs QuickSort) Sort(data []int) []int {
    result := make([]int, len(data))
    copy(result, data)
    
    qs.quickSort(result, 0, len(result)-1)
    return result
}

func (qs QuickSort) quickSort(arr []int, low, high int) {
    if low < high {
        pi := qs.partition(arr, low, high)
        qs.quickSort(arr, low, pi-1)
        qs.quickSort(arr, pi+1, high)
    }
}

func (qs QuickSort) partition(arr []int, low, high int) int {
    pivot := arr[high]
    i := low - 1
    
    for j := low; j < high; j++ {
        if arr[j] < pivot {
            i++
            arr[i], arr[j] = arr[j], arr[i]
        }
    }
    arr[i+1], arr[high] = arr[high], arr[i+1]
    return i + 1
}

// 排序上下文
type Sorter struct {
    strategy SortStrategy
}

func NewSorter(strategy SortStrategy) *Sorter {
    return &Sorter{strategy: strategy}
}

func (s *Sorter) SetStrategy(strategy SortStrategy) {
    s.strategy = strategy
}

func (s *Sorter) Sort(data []int) []int {
    fmt.Printf("使用 %s 进行排序\n", s.strategy.Name())
    return s.strategy.Sort(data)
}

// 计算策略接口
type DiscountStrategy interface {
    Calculate(amount float64) float64
    Description() string
}

// 无折扣策略
type NoDiscount struct{}

func (nd NoDiscount) Calculate(amount float64) float64 {
    return amount
}

func (nd NoDiscount) Description() string {
    return "无折扣"
}

// 百分比折扣策略
type PercentageDiscount struct {
    percentage float64
}

func NewPercentageDiscount(percentage float64) *PercentageDiscount {
    return &PercentageDiscount{percentage: percentage}
}

func (pd PercentageDiscount) Calculate(amount float64) float64 {
    return amount * (1 - pd.percentage/100)
}

func (pd PercentageDiscount) Description() string {
    return fmt.Sprintf("%.1f%%折扣", pd.percentage)
}

// 固定金额折扣策略
type FixedDiscount struct {
    amount float64
}

func NewFixedDiscount(amount float64) *FixedDiscount {
    return &FixedDiscount{amount: amount}
}

func (fd FixedDiscount) Calculate(amount float64) float64 {
    result := amount - fd.amount
    if result < 0 {
        return 0
    }
    return result
}

func (fd FixedDiscount) Description() string {
    return fmt.Sprintf("减免%.2f元", fd.amount)
}

// 购物车
type ShoppingCart struct {
    items    []float64
    discount DiscountStrategy
}

func NewShoppingCart() *ShoppingCart {
    return &ShoppingCart{
        items:    make([]float64, 0),
        discount: NoDiscount{},
    }
}

func (sc *ShoppingCart) AddItem(price float64) {
    sc.items = append(sc.items, price)
}

func (sc *ShoppingCart) SetDiscount(strategy DiscountStrategy) {
    sc.discount = strategy
}

func (sc *ShoppingCart) Total() float64 {
    var sum float64
    for _, price := range sc.items {
        sum += price
    }
    return sc.discount.Calculate(sum)
}

func (sc *ShoppingCart) Summary() {
    var originalTotal float64
    for _, price := range sc.items {
        originalTotal += price
    }
    
    finalTotal := sc.Total()
    saved := originalTotal - finalTotal
    
    fmt.Printf("商品总价: %.2f\n", originalTotal)
    fmt.Printf("折扣策略: %s\n", sc.discount.Description())
    fmt.Printf("最终价格: %.2f\n", finalTotal)
    if saved > 0 {
        fmt.Printf("节省金额: %.2f\n", saved)
    }
}

func main() {
    fmt.Println("=== 排序策略演示 ===")
    
    data := []int{64, 34, 25, 12, 22, 11, 90}
    fmt.Printf("原始数据: %v\n", data)
    
    // 使用不同的排序策略
    sorter := NewSorter(BubbleSort{})
    sorted1 := sorter.Sort(data)
    fmt.Printf("排序结果: %v\n", sorted1)
    
    sorter.SetStrategy(QuickSort{})
    sorted2 := sorter.Sort(data)
    fmt.Printf("排序结果: %v\n", sorted2)
    
    fmt.Println("\n=== 折扣策略演示 ===")
    
    // 创建购物车
    cart := NewShoppingCart()
    cart.AddItem(100.0)
    cart.AddItem(50.0)
    cart.AddItem(75.0)
    
    fmt.Println("无折扣:")
    cart.Summary()
    
    fmt.Println("\n10%折扣:")
    cart.SetDiscount(NewPercentageDiscount(10))
    cart.Summary()
    
    fmt.Println("\n减免30元:")
    cart.SetDiscount(NewFixedDiscount(30))
    cart.Summary()
}

🎓 小结

本章我们深入学习了 Go 语言的接口:

  • 接口基础:接口定义、隐式实现、多态性
  • 空接口:interface{} 的使用和类型断言
  • 接口组合:接口嵌套和组合模式
  • 设计模式:适配器模式、策略模式的实现
  • 实际应用:数据库抽象层、业务策略设计
  • 最佳实践:接口设计原则和使用技巧

接口是 Go 语言实现多态和代码复用的核心机制,掌握接口设计对于编写优雅的 Go 代码至关重要。


接下来,我们将学习 Go 错误处理,了解 Go 语言独特的错误处理机制。

接口设计建议

  • 接口应该小而专注,遵循单一职责原则
  • 优先组合小接口而不是设计大接口
  • 使用接口实现依赖注入和模块解耦
  • 合理使用空接口,避免过度使用

本站内容仅供学习和研究使用。