Go 接口使用

devtools/2025/3/4 2:46:37/

个人学习笔记

接口作用

1. 实现多态

多态允许不同的类型通过实现相同的接口,以统一的方式进行处理。这使得代码更加灵活和可扩展,提高了代码的复用性。

示例代码

package mainimport ("fmt"
)// 定义一个接口
type Speaker interface {Speak() string
}// 定义一个Dog结构体
type Dog struct{}// 实现Speaker接口的Speak方法
func (d Dog) Speak() string {return "Woof!"
}// 定义一个Cat结构体
type Cat struct{}// 实现Speaker接口的Speak方法
func (c Cat) Speak() string {return "Meow!"
}// 一个接受Speaker接口类型参数的函数
func makeSound(s Speaker) {fmt.Println(s.Speak())
}func main() {dog := Dog{}cat := Cat{}makeSound(dog)makeSound(cat)
}
代码逐行解释
type Speaker interface {
  • type:这是 Go 语言中用于定义新类型的关键字。
  • Speaker:这是新定义的接口的名称。按照 Go 语言的命名规范,接口名通常使用描述性的名称,并且首字母大写表示该接口是可导出的(可以被其他包使用)。
  • interface:该关键字用于声明一个接口类型。
    Speak() string
  • Speak:这是接口中定义的方法的名称。
  • ():这对括号表明 Speak 是一个方法,而不是一个字段或其他类型的成员。在 Go 语言中,方法是与特定类型关联的函数,() 用于指定方法的参数列表。这里 () 为空,表示该方法不接受任何参数。
  • string:这是 Speak 方法的返回值类型,意味着该方法会返回一个字符串。
}

这是接口定义的结束符号。

为什么 Speak 后面要加 ()

在 Go 语言里,方法是与特定类型关联的函数,而函数的定义需要明确其参数列表和返回值类型。() 是用来表示参数列表的,即使方法没有参数,也必须使用 () 来表明这是一个方法的定义。

如果 Speak 后面不加 (),代码就会变成这样:

type Speaker interface {Speak string
}

此时,Speak 会被视为接口中的一个字段,而不是一个方法。字段是用于存储数据的,而方法是用于执行操作的,两者的用途完全不同。

解释:在上述代码中,DogCat结构体都实现了Speaker接口的Speak方法。makeSound函数接受一个Speaker接口类型的参数,因此可以传入DogCat类型的对象,以统一的方式调用它们的Speak方法,实现了多态。

2. 解耦代码

接口可以将抽象和实现分离,降低代码之间的耦合度。调用方只需要依赖接口,而不需要依赖具体的实现类型,使得代码更加易于维护和扩展。

示例代码

package mainimport ("fmt"
)// 定义一个接口
type Storage interface {Save(data string)Load() string
}// 定义一个FileStorage结构体
type FileStorage struct{}// 实现Storage接口的Save方法
func (fs FileStorage) Save(data string) {fmt.Printf("Saving data '%s' to file.\n", data)
}// 实现Storage接口的Load方法
func (fs FileStorage) Load() string {return "Data loaded from file."
}// 定义一个MemoryStorage结构体
type MemoryStorage struct{}// 实现Storage接口的Save方法
func (ms MemoryStorage) Save(data string) {fmt.Printf("Saving data '%s' to memory.\n", data)
}// 实现Storage接口的Load方法
func (ms MemoryStorage) Load() string {return "Data loaded from memory."
}// 一个使用Storage接口的函数
func processData(s Storage) {s.Save("Sample data")result := s.Load()fmt.Println(result)
}func main() {fileStorage := FileStorage{}memoryStorage := MemoryStorage{}processData(fileStorage)processData(memoryStorage)
}

解释processData函数依赖于Storage接口,而不是具体的存储实现类型(如FileStorageMemoryStorage)。这样,当需要更换存储方式时,只需要实现Storage接口的新类型,而不需要修改processData函数的代码,降低了代码的耦合度。

3. 提供抽象层

接口可以为一组相关的操作提供一个抽象层,隐藏具体的实现细节,使得代码更加简洁和易于理解。

示例代码

package mainimport ("fmt"
)// 定义一个接口
type Database interface {Connect()Query(query string) []stringClose()
}// 一个使用Database接口的函数
func performDatabaseOperations(db Database) {db.Connect()results := db.Query("SELECT * FROM users")fmt.Println(results)db.Close()
}

解释Database接口为数据库操作提供了一个抽象层,调用方只需要知道如何使用这些抽象的方法,而不需要了解具体数据库的连接、查询和关闭的实现细节。

4. 方便单元测试

在单元测试中,接口可以用于创建模拟对象,方便对代码进行隔离测试。

示例代码

package mainimport ("fmt""testing"
)// 定义一个接口
type Calculator interface {Add(a, b int) int
}// 定义一个具体的实现结构体
type RealCalculator struct{}// 实现Calculator接口的Add方法
func (rc RealCalculator) Add(a, b int) int {return a + b
}// 一个使用Calculator接口的函数
func calculateSum(c Calculator, a, b int) int {return c.Add(a, b)
}// 定义一个模拟对象
type MockCalculator struct{}// 实现Calculator接口的Add方法
func (mc MockCalculator) Add(a, b int) int {return 100 // 模拟返回值
}func TestCalculateSum(t *testing.T) {mockCalc := MockCalculator{}result := calculateSum(mockCalc, 1, 2)if result != 100 {t.Errorf("Expected 100, got %d", result)}
}func main() {realCalc := RealCalculator{}sum := calculateSum(realCalc, 3, 4)fmt.Println(sum)testing.Main(func(pat, str string) (bool, error) { return true, nil }, []testing.InternalTest{{"TestCalculateSum", TestCalculateSum},}, nil, nil)
}

解释:在单元测试中,通过创建MockCalculator结构体实现Calculator接口,可以模拟Add方法的返回值,从而对calculateSum函数进行隔离测试,而不需要依赖真实的计算逻辑。

案例代码解释

  1. 接口定义:定义了一个Shape接口,包含AreaPerimeter两个方法。
  2. 结构体定义:定义了RectangleCircle两个结构体。
  3. 接口实现:为RectangleCircle结构体分别实现了AreaPerimeter方法,从而实现了Shape接口。
  4. 接口使用:定义了一个PrintShapeInfo函数,接受一个Shape接口类型的参数,在main函数中分别传入RectangleCircle对象调用该函数。
package mainimport ("fmt"
)// 定义一个接口
type Shape interface {Area() float64Perimeter() float64
}// 定义一个矩形结构体
type Rectangle struct {Width  float64Height float64
}// 实现Shape接口的Area方法
func (r Rectangle) Area() float64 {return r.Width * r.Height
}// 实现Shape接口的Perimeter方法
func (r Rectangle) Perimeter() float64 {return 2 * (r.Width + r.Height)
}// 定义一个圆形结构体
type Circle struct {Radius float64
}// 实现Shape接口的Area方法
func (c Circle) Area() float64 {return 3.14 * c.Radius * c.Radius
}// 实现Shape接口的Perimeter方法
func (c Circle) Perimeter() float64 {return 2 * 3.14 * c.Radius
}// 一个接受Shape接口类型参数的函数
func PrintShapeInfo(s Shape) {fmt.Printf("Area: %.2f\n", s.Area())fmt.Printf("Perimeter: %.2f\n", s.Perimeter())
}func main() {// 创建一个矩形对象rect := Rectangle{Width: 5, Height: 3}// 创建一个圆形对象circle := Circle{Radius: 2}// 调用PrintShapeInfo函数,传入矩形对象fmt.Println("Rectangle Info:")PrintShapeInfo(rect)// 调用PrintShapeInfo函数,传入圆形对象fmt.Println("\nCircle Info:")PrintShapeInfo(circle)
}


http://www.ppmy.cn/devtools/164347.html

相关文章

图数据库Neo4j面试内容整理-Neo4j 数据库模型设计

Neo4j 数据库模型设计 是将实际业务问题转化为图数据模型的过程。与关系型数据库不同,Neo4j 是基于图的数据库,它通过 节点(Node)、关系(Relationship) 和 属性(Property) 来表示和存储数据。在 Neo4j 中,数据被建模为图结构,其中节点代表实体,关系代表实体之间的关…

Hive之正则表达式RLIKE详解及示例

目录 一、RLIKE 语法及核心特性 1. 基本语法 2. 核心特性 二、常见业务场景及示例 场景1:过滤包含特定模式的日志(如错误日志) 场景2:验证字段格式(如邮箱、手机号) 场景3:提取复杂文本中…

Git操作指南:分支合并、回退及其他重要操作

在软件开发的协作过程中,Git 作为一款强大的版本控制系统,能帮助开发者高效管理代码的各个版本和分支。本文将详细介绍 Git 中常见的分支合并、取消本地修改、回退操作等,并提供通俗易懂的解释和步骤指南。 一、分支合并 分支合并是 Git 工…

STM32外设深度解析:CAN总线与USB的协同设计与IO模拟实战

STM32外设深度解析:CAN总线与USB的协同设计与IO模拟实战 目录1. CAN总线硬件控制器:寄存器级的秘密1.1 BxCAN控制器的工作模式1.2 过滤器配置的量子纠缠2. USB的异步世界:协议栈与端点管理2.1 USB库的隐秘开销2.2 控制传输的黑暗森林3. 审判日:CAN与USB的共存可行性3.1 硬件…

【练习】【栈】牛客NC212914牛牛与后缀表达式

题目 给定牛牛一个后缀表达式s,计算它的结果,例如,11对应的后缀表达式为1#1#,‘#’作为操作数的结束符号。 其中,表达式中只含有‘’、’-‘、’*‘三种运算,不包含除法。 本题保证表达式一定合法&#xff…

【大模型系列篇】DeepSeek开源周,解锁AI黑科技

🔥 Day1:FlashMLA —— GPU推理加速器 专为处理长短不一的AI推理请求而生,就像给Hopper GPU装上了智能导航,让数据在芯片上跑出3000GB/s的"磁悬浮"速度。✅ 已支持BF16格式|580万亿次浮点运算/秒FlashMLA G…

通过Nginx负载均衡+Keepalived实现业务高可用

通过Nginx负载均衡和Keepalived可以实现业务的高可用,以下是详细的实现步骤: 环境准备 假设我们有3台服务器,IP地址分别为: 服务器1(Nginx Keepalived 主节点):192.168.1.100服务器2&#x…

Spring IOC DI

前言 继续复习,继续补博客,继续努力,继续温故知新。 IoC 与 DI 入门 在 Java 开发领域,Spring 框架无疑占据着举足轻重的地位。其中,IoC(控制反转)和 DI(依赖注入)更是…