day2加餐 Go 接口型函数的使用场景
文章目录
- 问题
- 价值
- 使用场景
- 其他语言类似特性
问题
在 动手写分布式缓存 - GeeCache day2 单机并发缓存 这篇文章中,有一个接口型函数的实现:
// A Getter loads data for a key.
type Getter interface {Get(key string) ([]byte, error)
}// A GetterFunc implements Getter with a function.
type GetterFunc func(key string) ([]byte, error)// Get implements Getter interface function
func (f GetterFunc) Get(key string) ([]byte, error) {return f(key)
}
这里呢,定义了一个接口 Getter
,只包含一个方法 Get(key string) ([]byte, error)
,紧接着定义了一个函数类型 GetterFunc
,GetterFunc
参数和返回值与 Getter
中 Get
方法是一致的。而且 GetterFunc
还定义了 Get
方式,并在 Get
方法中调用自己,这样就实现了接口 Getter
。所以 GetterFunc 是一个实现了接口的函数类型,简称为接口型函数。
接口型函数只能应用于接口内部只定义了一个方法的情况
,例如接口 Getter
内部有且只有一个方法 Get
。既然只有一个方法,为什么还要多此一举,封装为一个接口呢?定义参数的时候,直接用 GetterFunc
这个函数类型不就好了,让用户直接传入一个函数作为参数,不更简单吗?
所以呢,接口型函数的价值什么?
价值
我们想象这么一个使用场景,GetFromSource
的作用是从某数据源获取结果,接口类型 Getter
是其中一个参数,代表某数据源:
func GetFromSource(getter Getter, key string) []byte {buf, err := getter.Get(key)if err == nil {return buf}return nil
}
我们可以有多种方式调用该函数:
方式一:GetterFunc 类型的函数作为参数
GetFromSource(GetterFunc(func(key string) ([]byte, error) {return []byte(key), nil
}), "hello")
支持匿名函数,也支持普通的函数:
func test(key string) ([]byte, error) {return []byte(key), nil
}func main() {GetFromSource(GetterFunc(test), "hello")
}
将 test
强制类型转换为 GetterFunc
,GetterFunc
实现了接口 Getter
,是一个合法参数。这种方式适用于逻辑较为简单的场景。
方式二:实现了 Getter 接口的结构体作为参数
type DB struct{ url string}func (db *DB) Query(sql string, args ...string) string {// ...return "hello"
}func (db *DB) Get(key string) ([]byte, error) {// ...v := db.Query("SELECT NAME FROM TABLE WHEN NAME= ?", key)return []byte(v), nil
}func main() {GetFromSource(new(DB), "hello")
}
DB
实现了接口 Getter
,也是一个合法参数。这种方式适用于逻辑较为复杂的场景,如果对数据库的操作需要很多信息,地址、用户名、密码,还有很多中间状态需要保持,比如超时、重连、加锁等等。这种情况下,更适合封装为一个结构体作为参数。
这样,既能够将普通的函数类型(需类型转换)作为参数,也可以将结构体作为参数,使用更为灵活,可读性也更好,这就是接口型函数的价值。
使用场景
这个特性在 groupcache
等大量的 Go
语言开源项目中被广泛使用,标准库中用得也不少,net/http
的 Handler
和 HandlerFunc
就是一个典型。
我们先看一下 Handler
的定义:
type Handler interface {ServeHTTP(ResponseWriter, *Request)
}
type HandlerFunc func(ResponseWriter, *Request)func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {f(w, r)
}
摘自 Go
语言源代码 net/http/server.go
我们可以 http.Handle
来映射请求路径和处理函数,Handle
的定义如下:
func Handle(pattern string, handler Handler)
第二个参数是即接口类型 Handler
,我们可以这么用。
func home(w http.ResponseWriter, r *http.Request) {w.WriteHeader(http.StatusOK)_, _ = w.Write([]byte("hello, index page"))
}func main() {http.Handle("/home", http.HandlerFunc(home))_ = http.ListenAndServe("localhost:8000", nil)
}
通常,我们还会使用另外一个函数 http.HandleFunc
,HandleFunc
的定义如下:
func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
第二个参数是一个普通的函数类型,那可以直接将 home
传递给 HandleFunc
:
func main() {http.HandleFunc("/home", home)_ = http.ListenAndServe("localhost:8000", nil)
}
那如果我们看过 HandleFunc
的内部实现的话,就会知道两种写法是完全等价的,内部将第二种写法转换为了第一种写法。
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {if handler == nil {panic("http: nil handler")}mux.Handle(pattern, HandlerFunc(handler))
}
如果你仔细观察,会发现 http.ListenAndServe
的第二个参数也是接口类型 Handler
,我们使用了标准库 net/http
内置的路由,因此呢,传入的值是 nil
。那如果这个地方我们传入的是一个实现了 Handler
接口的结构体呢?就可以完全托管所有的 HTTP
请求,后续怎么路由,怎么处理,请求前后增加什么功能,都可以自定义了。慢慢地,就变成了一个功能丰富的 Web
框架了。如果你感兴趣呢,可以阅读 7天用Go从零实现Web框架Gee教程。
其他语言类似特性
如果有 Java
编程经验的同学可能比较有感触。Java 1.5
中是不支持直接传入函数的,参数要么是接口,要么是对象。举一个最简单的例子,列表自定义排序时,需要实现一个匿名的 Comparator
类,重写 compare
方法。
Collections.sort(list, new Comparator<Integer>(){@Overridepublic int compare(Integer o1, Integer o2) {return o2 - o1;}
});
Java 1.8
中引入了大量的函数式编程的特性,其中 lambda
表达式和函数式接口就是一个很好的简化 Java
写法的特性。Java 1.8
中,上述的例子可以简化为:
Collections.sort(list, (Integer o1, Integer o2) -> o2 - o1 );
即从需要构造一个匿名对象简化为只需要一个lambda
函数表达式,可以认为是面向对象与函数式编程的一种结合。同样地,这种写法只支持只定义了一个方法的接口类型,因为只定义了一个方法的接口,就会很明确传入进来的函数对应接口中的哪个方法。
正是这种结合,可以达到实现相同代码,代码量更少的目的。
相关文章:
day2加餐 Go 接口型函数的使用场景
文章目录 问题价值使用场景其他语言类似特性 问题 在 动手写分布式缓存 - GeeCache day2 单机并发缓存 这篇文章中,有一个接口型函数的实现: // A Getter loads data for a key. type Getter interface {Get(key string) ([]byte, error) }// A Getter…...

摄像头 RN6752v1 视频采集卡
摄像头 AHD倒车摄像头比较好,AHD英文全名Analog High Definition,即模拟高清,拥有比较好的分辨率与画面质感。 RN6752v1 GQW AKKY2 usb 采集卡 FHD(1080p)、HD(720p)和D1(480i&am…...

记录vivado自带IP iBert眼图近端回环
记录利用vivado自带IP核工具测试信号质量 ibert是测试眼图的工具,在使用的时候并不用改太多的内容,只需要注意参考时钟及所需要的引脚即可。由于条件的限制,并没有使用光纤和电缆进行连接进行外部回环,仅使用内部回环做测试&…...

js | Core
http://dmitrysoshnikov.com/ecmascript/javascript-the-core/ Object 是什么? 属性[[prototype]]对象。 例如,下面的,son是对象,foo不是对象。打印出来的son,能看到有一个prototype 对象。 prototype vs _proto_ v…...
Log4J reminder
Java JNDI and Log injection https://docs.oracle.com/javase/jndi/tutorial/ See also https://telegra.ph/Log4J-Vulnerability-Explained-07-21...

Unity XR Interaction Toolkit(VR、AR交互工具包)记录安装到开发的流程,以及遇到的常见问题(一)!
提示:文章有错误的地方,还望诸位大神不吝指教! 文章目录 前言一、XR Interaction Toolkit是什么?二、跨平台交互三、 AR 功能四、XR Interaction Toolkit的特点五、XR Interaction Toolkit 示例总结 前言 随着VR行业的发展&#…...

MongoDB文档整理
过往mongodb文档: https://blog.csdn.net/qq_46921028/article/details/123361633https://blog.csdn.net/qq_46921028/article/details/131136935https://blog.csdn.net/qq_46921028/article/details/139247847 1. MongoDB前瞻 1、MongoDB概述: MongoDB是…...

【AI学习】关于Scaling Law的相关学习
一、苦涩的教训 首先,学习一段重要话语: The biggest lesson that can be read from 70 years of AI research is that general methods that leverage computation are ultimately the most effective, and by a large margin. 从70年的人工智能研究中…...
学习小记-Kafka相较于其他MQ有啥优势?
Kafka 相比于 RocketMQ 有以下几个优势: 1. 高吞吐量和低延迟: Kafka 以其出色的 I/O 性能和分布式架构设计,能够实现极高的吞吐量,每秒数百万的消息处理能力,适合大规模数据流处理。同时,Kafka 设计为…...

技能 | postman接口测试工具安装及使用
哈喽小伙伴们大家好!今天来给大家分享一款轻量级,高效好用的接口测试工具-postman. Postman是一个流行的API开发工具,主要用于测试、开发和文档化API。以下是关于Postman的介绍及其主要使用场景: Postman介绍: 1. 功能丰富的API客户端&#…...

移动UI:任务中心的作用,该如何设计更合理?
任务中心是移动应用中用于展示和管理用户待办任务、提醒事项、用户福利、打卡签到等内容的功能模块。合理设计任务中心可以提升用户体验和工作效率。 以下是一些设计任务中心的合理建议: 1. 易于查看和管理: 任务中心的设计应该使用户能够快速、直观地…...

pytorch学习(十)优化函数
优化函数主要有,SGD, Adam,RMSProp这三种,并且有lr学习率,momentum动量,betas等参数需要设置。 通过这篇文章,可以学到pytorch中的优化函数的使用。 1.代码 代码参考《python深度学习-基于pytorch》&…...
Ubuntu22.04:安装Samba
1.安装Samba服务 $ sudo apt install samba samba-common 2.创建共享目录 $ mkdir /home/xxx/samba $ chmod 777 /home/xxx/samba 3.将用户加入到Samba服务中 $ sudo smbpasswd -a xxx 设置用户xxx访问Samba的密码 4.配置Samba服务 $ sudo vi /etc/samba/smb.conf 在最后加入 …...
Powershell 使用介绍
0 Preface/Foreword 0.1 参考文档 Starting Windows PowerShell - PowerShell | Microsoft Learn 1 Powershell 介绍 2 命令介绍 2.1 新建文件夹 New-Item -Path C:\GitLab-Runner -ItemType Directory 2.2 切换路径 cd C:\GitLab-Runner 2.3 下载文件 Invoke-WebRequ…...

【Langchain大语言模型开发教程】记忆
🔗 LangChain for LLM Application Development - DeepLearning.AI 学习目标 1、Langchain的历史记忆 ConversationBufferMemory 2、基于窗口限制的临时记忆 ConversationBufferWindowMemory 3、基于Token数量的临时记忆 ConversationTokenBufferMemory 4、基于历史…...

最新Qt6的下载与成功安装详细介绍
引言 Qt6 是一款强大的跨平台应用程序开发框架,支持多种编程语言,最常用的是C。Qt6带来了许多改进和新功能,包括对C17的支持、增强的QML和UI技术、新的图形架构,以及构建系统方面的革新。本文将指导你如何在Windows平台上下载和安…...

LeetCode 热题 HOT 100 (001/100)【宇宙最简单版】
【链表】 No. 0160 相交链表 【简单】👉力扣对应题目指路 希望对你有帮助呀!!💜💜 如有更好理解的思路,欢迎大家留言补充 ~ 一起加油叭 💦 欢迎关注、订阅专栏 【力扣详解】谢谢你的支持&#x…...

Ubantu 使用 docker 配置 + 远程部署 + 远程开发
大家好我是苏麟 , Ubantu 一些配置 . 视频 : 服务器很贵?搞台虚拟机玩玩!保姆级 Linux 远程开发教程_哔哩哔哩_bilibili Docker安装及配置 安装命令 : sudo apt install docker.io 查看版本号 : docker -v 查看虚拟机地址命令 : ifconfig 虚拟机地址 或…...

应用层自定义协议与序列化
个人主页:Lei宝啊 愿所有美好如期而遇 协议 简单来说,就是通信双方约定好的结构化的数据。 序列化与反序列化 我们通过一个问题引入这个概念,假如我们要实现一个网络版的计算器,那么现在有两种方案,第一种&#x…...

Python学习笔记—100页Opencv详细讲解教程
目录 1 创建和显示窗口... - 4 - 2 加载显示图片... - 6 - 3 保存图片... - 7 - 4 视频采集... - 8 - 5视频录制... - 11 - 6 控制鼠标... - 12 - 7 TrackBar 控件... - 14 - 8.RGB和BGR颜色空间... - 16 - 9.HSV和HSL和YUV.. - 17 - 10 颜色空间的转化... - 18 - …...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...

【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
Qt Widget类解析与代码注释
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码,写上注释 当然可以!这段代码是 Qt …...

如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...

Linux 中如何提取压缩文件 ?
Linux 是一种流行的开源操作系统,它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间,使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的,要在 …...

基于IDIG-GAN的小样本电机轴承故障诊断
目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) 梯度归一化(Gradient Normalization) (2) 判别器梯度间隙正则化(Discriminator Gradient Gap Regularization) (3) 自注意力机制(Self-Attention) 3. 完整损失函数 二…...

力扣热题100 k个一组反转链表题解
题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...
【Elasticsearch】Elasticsearch 在大数据生态圈的地位 实践经验
Elasticsearch 在大数据生态圈的地位 & 实践经验 1.Elasticsearch 的优势1.1 Elasticsearch 解决的核心问题1.1.1 传统方案的短板1.1.2 Elasticsearch 的解决方案 1.2 与大数据组件的对比优势1.3 关键优势技术支撑1.4 Elasticsearch 的竞品1.4.1 全文搜索领域1.4.2 日志分析…...