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 语言独特的错误处理机制。
接口设计建议
- 接口应该小而专注,遵循单一职责原则
- 优先组合小接口而不是设计大接口
- 使用接口实现依赖注入和模块解耦
- 合理使用空接口,避免过度使用