Go Interfaces

Interfaces are one of the most important features of Go. They define behavioral contracts for objects. Go interfaces are implemented implicitly, which makes code more flexible and extensible.

📋 Interface Basics

Interface Definition and Implementation

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())
    }
}

Empty Interface

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("不是字符串类型")
    }
}

🎯 Interface Composition

Interface Nesting

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)
}

Interface Adapter Pattern

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)
}

🔧 Practical Application Examples

Database Interface Design

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)
}

Strategy Pattern Implementation

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()
}

🎓 Summary

In this chapter, we explored Go interfaces in depth:

  • Interface basics: interface definition, implicit implementation, polymorphism
  • Empty interface: using interface{} and type assertions
  • Interface composition: interface nesting and composition patterns
  • Design patterns: adapter pattern and strategy pattern implementations
  • Practical applications: database abstraction layers and business strategy design
  • Best practices: interface design principles and usage tips

Interfaces are the core mechanism for polymorphism and code reuse in Go. Mastering interface design is essential for writing elegant Go code.


Next, we will learn about Go Error Handling to understand Go's unique error handling mechanism.

::: tip Interface Design Recommendations

  • Interfaces should be small and focused, following the single responsibility principle
  • Prefer composing small interfaces rather than designing large ones
  • Use interfaces to implement dependency injection and module decoupling
  • Use empty interfaces judiciously and avoid overusing them :::