Go 接口使用
个人学习笔记
接口作用
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 会被视为接口中的一个字段,而不是一个方法。字段是用于存储数据的,而方法是用于执行操作的,两者的用途完全不同。
解释:在上述代码中,Dog和Cat结构体都实现了Speaker接口的Speak方法。makeSound函数接受一个Speaker接口类型的参数,因此可以传入Dog或Cat类型的对象,以统一的方式调用它们的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接口,而不是具体的存储实现类型(如FileStorage或MemoryStorage)。这样,当需要更换存储方式时,只需要实现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函数进行隔离测试,而不需要依赖真实的计算逻辑。
案例代码解释
- 接口定义:定义了一个
Shape接口,包含Area和Perimeter两个方法。 - 结构体定义:定义了
Rectangle和Circle两个结构体。 - 接口实现:为
Rectangle和Circle结构体分别实现了Area和Perimeter方法,从而实现了Shape接口。 - 接口使用:定义了一个
PrintShapeInfo函数,接受一个Shape接口类型的参数,在main函数中分别传入Rectangle和Circle对象调用该函数。
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)
}
相关文章:
Go 接口使用
个人学习笔记 接口作用 1. 实现多态 多态允许不同的类型通过实现相同的接口,以统一的方式进行处理。这使得代码更加灵活和可扩展,提高了代码的复用性。 示例代码: package mainimport ("fmt" )// 定义一个接口 type Speaker int…...
题解 | 牛客周赛82 Java ABCDEF
目录 题目地址 做题情况 A 题 B 题 C 题 D 题 E 题 F 题 牛客竞赛主页 题目地址 牛客竞赛_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ 做题情况 A 题 判断字符串第一个字符和第三个字符是否相等 import java.io.*; import java.math.*; import java.u…...
命名管道——进程间通信
个人主页:敲上瘾-CSDN博客 匿名管道:进程池的制作(linux进程间通信,匿名管道... ...)-CSDN博客 一、命名管道的使用 1.创建命名管道 1.1.在命令行中: 创建: mkfifo 管道名 删除:…...
高频 SQL 50 题(基础版)_1141. 查询近30天活跃用户数
1141. 查询近30天活跃用户数 select activity_date day,count(distinct user_id) active_users from Activity where (activity_date<2019-07-27 and activity_date>DATE_sub(2019-07-27,INTERVAL 30 DAY)) group by(activity_date)...
Yocto + 树莓派摄像头驱动完整指南
—— 从驱动配置、Yocto 构建,到 OpenCV 实战 在树莓派上运行摄像头,在官方的 Raspberry Pi OS 可能很简单,但在 Yocto 项目中,需要手动配置驱动、设备树、软件依赖 才能确保摄像头正常工作。本篇文章从 BSP 驱动配置、Yocto 关键…...
seaborn中文乱码
在进行matplotlib画图的时候,经常会出现中文乱码的问题,这主要是默认的文件不支持中文,可以在代码中显示指定。解决方法: import seaborn as sns import matplotlib.pyplot as pltplt.rcParams["font.sans-serif"] ["SimHei"] # …...
函数的特殊形式——递归函数
C递归函数入门指南:从概念到实践 1. 什么是递归? 递归是指函数直接或间接调用自身的过程,就像照镜子时影像无限反射,通过不断分解问题解决问题 适用场景: 问题可分解为相同子问题(如阶乘、斐波那契数列…...
计算最大海岛面积
最大海岛面积问题的不同解法 问题举例 给定一个包含了一些 0 和 1 的非空二维数组 matrix 。 一个岛屿是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在水平或者竖直方向上相邻。你可以假设matrix的四个边缘都被 0(代表水&am…...
list的两个实现类
ArrayList:适用于需要频繁随机访问元素 LinkedList:适用于需要频繁进行插入和删除操作,尤其是在列表的头部或尾部进行操作 二者的用法基本一致,只是时间和空间复杂度不同 List<Integer> arrayList new ArrayList<>…...
Spark核心之02:RDD、算子分类、常用算子
spark内存计算框架 一、目标 深入理解RDD弹性分布式数据集底层原理掌握RDD弹性分布式数据集的常用算子操作 二、要点 ⭐️1. RDD是什么 RDD(Resilient Distributed Dataset)叫做**弹性分布式数据集,是Spark中最基本的数据抽象,…...
配置Nginx日志url encode问题
文章目录 配置Nginx日志url encode问题方法1-lua方法2-set-misc-nginx-module 配置Nginx日志url encode问题 问题描述: 当自定义日志输出格式,需要输出http请求中url参数时,如果参数中包含中文,是会进行url encode的,…...
[Windows] 批量为视频或者音频生成字幕 video subtitle master 1.5.2
Video Subtitle Master 1.5.2 介绍 Video Subtitle Master 1.5.2 是一款功能强大的客户端工具,能够批量为视频或音频生成字幕,还支持批量将字幕翻译成其他语言。该工具具有跨平台性,无论是 mac 系统还是 windows 系统都能使用。 参考原文&a…...
AIP-158 分页
编号158原文链接AIP-158: Pagination状态批准创建日期2019-02-18更新日期2019-02-18 API通常需要提供数据集,最常见的是 List 标准方法。但集合大小往往是不受控制的,会随着时间增长,提高了查找时间和通过网络传输的应答大小。因此对集合进行…...
进来了解一下python的深浅拷贝
深浅拷贝是什么:在Python中,理解深拷贝(deep copy)和浅拷贝(shallow copy)对于处理复杂的数据结构,如列表、字典或自定义对象,是非常重要的。这两种拷贝方式决定了数据在内存中的复制…...
第三阶段-产品方面的技术疑难
一、虚拟机和容器的区别? 虚拟机(Virtual Machine,VM)和容器(Container)都是用于隔离和运行应用程序的技术,但它们在实现方式、性能、资源消耗和适用场景上有显著区别。以下是虚拟机和容器的主…...
safetensors PyTorchModelHubMixin 加载模型
2025.03.03测试ok from safetensors.torch import load_fileimport yamlwith open("configs/maggie_image.yaml", r, encodingutf8) as file: # utf8可识别中文data yaml.safe_load(file)class Config:def __init__(self, **kwargs):for key, value in kwargs.item…...
解锁GPM 2.0「卡顿帧堆栈」|代码示例与实战分析
每个游戏开发者都有一个共同的愿望,那就是能够在无需复现玩家反馈的卡顿现象时,快速且准确地定位卡顿的根本原因。为了实现这一目标,UWA GPM 2.0推出了全新功能 - 卡顿帧堆栈,旨在为开发团队提供高效、精准的卡顿分析工具。在这篇…...
Transformer架构
核心原理 自注意力机制 通过计算输入序列中每个位置与其他位置的关联权重(Query-Key匹配),动态聚合全局信息,解决了传统RNN/CNN的长距离依赖问题。 实现公式:Attention(Q,K,V)softmax(QKTdk)VAttention(…...
微服务,服务治理nacos,负载均衡LOadBalancer,OpenFeign
1.微服务 简单来说,微服务架构风格[1]是一种将一个单一应用程序开发为一组小型服务的方法,每个服务运行在 自己的进程中,服务间通信采用轻量级通信机制(通常用HTTP资源API)。这些服务围绕业务能力构建并 且可通过全自动部署机制独立部署。这…...
服务器租用:静态BGP和动态BGP分别指什么?
今天小编主要来带大家一起了解一下静态BGP和动态BGP分别是指什么? BGP主要是用在不同网络之间进行交换路由信息的协议,通常是用在互联网当中,而静态BGP和动态BGP是两种不同的方法来配置BGP路由,静态BGP路由是由手动配置的…...
栈和队列的模拟实现
文章目录 一. 回顾栈和队列二. stack的模拟实现stack.hstack.cpp 三. queue的模拟实现queue.htest.cpp 四. 了解dequeuevector和list都有各自的缺陷deque 总结 一. 回顾栈和队列 回顾一下栈和队列 栈:stack:后进先出 _ 队列:queue…...
CSDN博客写作教学(五):从写作到个人IP的体系化构建(完结篇)
导语 (第一篇)Markdown编辑器基础 (第二篇)Markdown核心语法 (第三篇)文章结构化思维 (第四篇)标题优化与SEO实战 通过前四篇教程,你已掌握技术写作的“术”——排版、标题、流量与数据。但真正的价值在于将技能升维为“道”:用技术博客为支点,撬动个人品牌与职业发…...
Django 项目模块化开发指南:实现 Vue 风格的组件化
在 Django 项目中,我们经常需要 复用 HTML 代码,避免重复编写相同的模板。例如,博客系统中,博客列表页 和 文章详情页 可能都有相同的 导航栏、模态框、页脚 等。如何像 Vue 一样进行 模块化开发,让代码更加清晰、可维护呢? 本文将详细介绍 Django 的模板继承 和 {% incl…...
unity pico开发 四 物体交互 抓取 交互层级
文章目录 手部设置物体交互物体抓取添加抓取抓取三种类型抓取点偏移抓取事件抓取时不让物体吸附到手部 射线抓取交互层级 手部设置 为手部(LeftHandController)添加XRDirInteractor脚本 并添加一个球形碰撞盒,勾选isTrigger,调整大小为0.1 …...
opencv 模板匹配方法汇总
在OpenCV中,模板匹配是一种在较大图像中查找特定模板图像位置的技术。OpenCV提供了多种模板匹配方法,通过cv2.matchTemplate函数实现,该函数支持的匹配方式主要有以下6种,下面详细介绍每种方法的原理、特点和适用场景。 1. cv2.T…...
【PromptCoder + Cursor】利用AI智能编辑器快速实现设计稿
【PromptCoder Cursor】利用AI智能编辑器快速实现设计稿 官网:PromptCoder 在现代前端开发中,将设计稿转化为可运行的代码是一项耗时的工作。然而,借助人工智能工具,这一过程可以变得更加高效和简单。本文将介绍如何结合 Promp…...
MySQL面试01
MySQL 索引的最左原则 🍰 最左原则本质 ͟͟͞͞( •̀д•́) 想象复合索引是电话号码簿! 索引 (a,b,c) 的排列顺序: 先按a排序 → a相同按b排序 → 最后按c排序 生效场景三连: 1️⃣ WHERE a1 ✅ 2️⃣ WHERE a1 AND b2 ✅ 3️…...
webpack一篇
目录 一、构建工具 1.1简介 二、Webpack 2.1概念 2.2使用步骤 2.3配置文件(webpack.config.js) mode entry output loader plugin devtool 2.4开发服务器(webpack-dev-server) grunt/glup的对比 三、Vite 3.1概念 …...
健康饮食,健康早餐
营养早餐最好包含4大类食物:谷薯类;碳水;蛋白质;膳食纤维。 1.优质碳水 作用:提供持久的能量,避免血糖大幅波动等 例如:全麦面包、红薯🍠、玉米🌽、土豆🥔、…...
常见的排序算法 【复习笔记】
注意: 1. 后面的排序算法实现都只考虑升序,对于逆序,只有知道原理,实现很容易 2. 案例题: 题目描述:将读入的 N 个数从小到大输出 ( 1 < N <10e5) 输入描述:第一行一个正整数 N 第二行…...
