Go语言核心36讲43-----io包中接口的好处与优势
上一篇文章中,我主要讲到了io.Reader的扩展接口和实现类型。当然,io代码包中的核心接口不止io.Reader一个。
我们基于它引出的一条主线,只是io包类型体系中的一部分。我们很有必要再从另一个角度去探索一下,以求对io包有更加全面的了解。
下面的一个问题就与此有关。
知识扩展
问题:io包中的接口都有哪些?它们之间都有着怎样的关系?
我们可以把没有嵌入其他接口并且只定义了一个方法的接口叫做简单接口。在io包中,这样的接口一共有11个。
在它们之中,有的接口有着众多的扩展接口和实现类型,我们可以称之为核心接口。io包中的核心接口只有3个,它们是:io.Reader、io.Writer和io.Closer。
我们还可以把io包中的简单接口分为四大类。这四大类接口分别针对于四种操作,即:读取、写入、关闭和读写位置设定。前三种操作属于基本的I/O操作。
关于读取操作,我们在前面已经重点讨论过核心接口io.Reader。它在io包中有5个扩展接口,并有6个实现类型。除了它,这个包中针对读取操作的接口还有不少。我们下面就来梳理一下。
首先来看io.ByteReader和io.RuneReader这两个简单接口。它们分别定义了一个读取方法,即:ReadByte和ReadRune。
但与io.Reader接口中Read方法不同的是,这两个读取方法分别只能够读取下一个单一的字节和Unicode字符。
我们之前讲过的数据类型strings.Reader和bytes.Buffer都是io.ByteReader和io.RuneReader的实现类型。
不仅如此,这两个类型还都实现了io.ByteScanner接口和io.RuneScanner接口。
io.ByteScanner接口内嵌了简单接口io.ByteReader,并定义了额外的UnreadByte方法。如此一来,它就抽象出了一个能够读取和读回退单个字节的功能集。
与之类似,io.RuneScanner内嵌了简单接口io.RuneReader,并定义了额外的UnreadRune方法。它抽象的是可以读取和读回退单个Unicode字符的功能集。
再来看io.ReaderAt接口。它也是一个简单接口,其中只定义了一个方法ReadAt。与我们在前面说过的读取方法都不同,ReadAt是一个纯粹的只读方法。
它只去读取其所属值中包含的字节,而不对这个值进行任何的改动,比如,它绝对不能去修改已读计数的值。这也是io.ReaderAt接口与其实现类型之间最重要的一个约定。
因此,如果仅仅并发地调用某一个值的ReadAt方法,那么安全性应该是可以得到保障的。
另外,还有一个读取操作相关的接口我们没有介绍过,它就是io.WriterTo。这个接口定义了一个名为WriteTo的方法。
千万不要被它的名字迷惑,这个WriteTo方法其实是一个读取方法。它会接受一个io.Writer类型的参数值,并会把其所属值中的数据读出并写入到这个参数值中。
与之相对应的是io.ReaderFrom接口。它定义了一个名叫ReadFrom的写入方法。该方法会接受一个io.Reader类型的参数值,并会从该参数值中读出数据,并写入到其所属值中。
值得一提的是,我们在前面用到过的io.CopyN函数,在复制数据的时候会先检测其参数src的值,是否实现了io.WriterTo接口。如果是,那么它就直接利用该值的WriteTo方法,把其中的数据拷贝给参数dst代表的值。
类似的,这个函数还会检测dst的值是否实现了io.ReaderFrom接口。如果是,那么它就会利用这个值的ReadFrom方法,直接从src那里把数据拷贝进该值。
实际上,对于io.Copy函数和io.CopyBuffer函数来说也是如此,因为它们在内部做数据复制的时候用的都是同一套代码。
你也看到了,io.ReaderFrom接口与io.WriterTo接口对应得很规整。实际上,在io包中,与写入操作有关的接口都与读取操作的相关接口有着一定的对应关系。下面,我们就来说说写入操作相关的接口。
首先当然是核心接口io.Writer。基于它的扩展接口除了有我们已知的io.ReadWriter、io.ReadWriteCloser和io.ReadWriteSeeker之外,还有io.WriteCloser和io.WriteSeeker。
我们之前提及的*io.pipe就是io.ReadWriter接口的实现类型。然而,在io包中并没有io.ReadWriteCloser接口的实现,它的实现类型主要集中在net包中。
除此之外,写入操作相关的简单接口还有io.ByteWriter和io.WriterAt。可惜,io包中也没有它们的实现类型。不过,有一个数据类型值得在这里提一句,那就是*os.File。
这个类型不但是io.WriterAt接口的实现类型,还同时实现了io.ReadWriteCloser接口和io.ReadWriteSeeker接口。也就是说,该类型支持的I/O操作非常的丰富。
io.Seeker接口作为一个读写位置设定相关的简单接口,也仅仅定义了一个方法,名叫Seek。
我在讲strings.Reader类型的时候还专门说过这个Seek方法,当时还给出了一个与已读计数估算有关的例子。该方法主要用于寻找并设定下一次读取或写入时的起始索引位置。
io包中有几个基于io.Seeker的扩展接口,包括前面讲过的io.ReadSeeker和io.ReadWriteSeeker,以及还未曾提过的io.WriteSeeker。io.WriteSeeker是基于io.Writer和io.Seeker的扩展接口。
我们之前多次提到的两个指针类型strings.Reader和io.SectionReader都实现了io.Seeker接口。顺便说一句,这两个类型也都是io.ReaderAt接口的实现类型。
最后,关闭操作相关的接口io.Closer非常通用,它的扩展接口和实现类型都不少。我们单从名称上就能够一眼看出io包中的哪些接口是它的扩展接口。至于它的实现类型,io包中只有io.PipeReader和io.PipeWriter。
总结
我们来总结一下这两篇的内容。在Go语言中,对接口的扩展是通过接口类型之间的嵌入来实现的,这也常被叫做接口的组合。而io代码包恰恰就可以作为接口扩展的一个标杆,它可以成为我们运用这种技巧时的一个参考标准。
在本文中,我根据接口定义的方法的数量以及是否有接口嵌入,把io包中的接口分为了简单接口和扩展接口。
同时,我又根据这些简单接口的扩展接口和实现类型的数量级,把它们分为了核心接口和非核心接口。
在io包中,称得上核心接口的简单接口只有3个,即:io.Reader、io.Writer和io.Closer。这些核心接口在Go语言标准库中的实现类型都在200个以上。
另外,根据针对的I/O操作的不同,我还把简单接口分为了四大类。这四大类接口针对的操作分别是:读取、写入、关闭和读写位置设定。
其中,前三种操作属于基本的I/O操作。基于此,我带你梳理了每个类别的简单接口,并讲解了它们在io包中的扩展接口,以及具有代表性的实现类型。

( io包中的接口体系)
除此之外,我还从多个维度为你描述了一些重要程序实体的功用和机理,比如:数据段读取器io.SectionReader、作为同步内存管道核心实现的io.pipe类型,以及用于数据拷贝的io.CopyN函数,等等。
我如此详尽且多角度的阐释,正是为了让你能够记牢io代码包中有着网状关系的接口和数据类型。我希望这个目的已经达到了,最起码,本文可以作为你深刻记忆它们的开始。
最后再强调一下,io包中的简单接口共有11个。其中,读取操作相关的接口有5个,写入操作相关的接口有4个,而与关闭操作有关的接口只有1个,另外还有一个读写位置设定相关的接口。
此外,io包还包含了9个基于这些简单接口的扩展接口。你需要在今后思考和实践的是,你在什么时候应该编写哪些数据类型实现io包中的哪些接口,并以此得到最大的好处。
思考题
今天的思考题是:io包中的同步内存管道的运作机制是什么?
Go语言核心36讲43-----io包中接口的好处与优势的更多相关文章
- Go语言核心36讲(Go语言实战与应用十八)--学习笔记
40 | io包中的接口和工具 (上) 我们在前几篇文章中,主要讨论了strings.Builder.strings.Reader和bytes.Buffer这三个数据类型. 知识回顾 还记得吗?当时我 ...
- Go语言核心36讲(Go语言进阶技术八)--学习笔记
14 | 接口类型的合理运用 前导内容:正确使用接口的基础知识 在 Go 语言的语境中,当我们在谈论"接口"的时候,一定指的是接口类型.因为接口类型与其他数据类型不同,它是没法被实 ...
- Go语言核心36讲(Go语言实战与应用二十二)--学习笔记
44 | 使用os包中的API (上) 我们今天要讲的是os代码包中的 API.这个代码包可以让我们拥有操控计算机操作系统的能力. 前导内容:os 包中的 API 这个代码包提供的都是平台不相关的 A ...
- Go语言核心36讲(Go语言实战与应用十九)--学习笔记
41 | io包中的接口和工具 (下) 上一篇文章中,我主要讲到了io.Reader的扩展接口和实现类型.当然,io代码包中的核心接口不止io.Reader一个. 我们基于它引出的一条主线,只是io包 ...
- Go语言核心36讲(Go语言实战与应用二十一)--学习笔记
43 | bufio包中的数据类型(下) 在上一篇文章中,我提到了bufio包中的数据类型主要有Reader.Scanner.Writer和ReadWriter.并着重讲到了bufio.Reader类 ...
- Go语言核心36讲(新年彩蛋)--学习笔记
新年彩蛋 | 完整版思考题答案 基础概念篇 Go 语言在多个工作区中查找依赖包的时候是以怎样的顺序进行的? 答:你设置的环境变量GOPATH的值决定了这个顺序.如果你在GOPATH中设置了多个工作区, ...
- Go语言核心36讲(Go语言实战与应用二十)--学习笔记
42 | bufio包中的数据类型 (上) 今天,我们来讲另一个与 I/O 操作强相关的代码包bufio.bufio是"buffered I/O"的缩写.顾名思义,这个代码包中的程 ...
- Go语言核心36讲(Go语言实战与应用二十三)--学习笔记
45 | 使用os包中的API (下) 我们在上一篇文章中.从"os.File类型都实现了哪些io包中的接口"这一问题出发,介绍了一系列的相关内容.今天我们继续围绕这一知识点进行扩 ...
- Go语言核心36讲(Go语言实战与应用三)--学习笔记
25 | 更多的测试手法 在本篇文章,我会继续为你讲解更多更高级的测试方法.这会涉及testing包中更多的 API.go test命令支持的,更多标记更加复杂的测试结果,以及测试覆盖度分析等等. 前 ...
- Go语言核心36讲(Go语言实战与应用四)--学习笔记
26 | sync.Mutex与sync.RWMutex 从本篇文章开始,我们将一起探讨 Go 语言自带标准库中一些比较核心的代码包.这会涉及这些代码包的标准用法.使用禁忌.背后原理以及周边的知识. ...
随机推荐
- 果汁 DI 介绍
Guice (英音同 'juice[果汁]') 是一个为 JDK8 及以上提供的轻量依赖注入框架. 目录 三级标题 三级标题 四级标题 三级标题 三级标题 /** * Animal */ interf ...
- IK分词器实现原理剖析 —— 一个小问题引发的思考
前言: 网上很多的文章都建议在使用IK分词器的时候,建立索引的时候使用ik_max_word模式:搜索的时候使用ik_smart模式.理由是max_word模式分词的结果会包含smart分词的结果,这 ...
- CMD和Entrypoint命令使用变量的用法
CMD 支持三种格式 CMD ["executable","param1","param2"] 使用 exec 执行,推荐方式: CMD c ...
- 监控Redis集群,有两种方法
前提条件 redis集群:已搭建三主三从(三台主机) prometheus.grafana已安装 三台主机ip: 192.168.0.39,192.168.0.164,192.168.0.68 第一种 ...
- 授予用户/用户组访问 Kubernetes 的一个名称空间
转载地址:https://www.kuboard.cn/learning/k8s-advanced/sec/rbac/auth-namespace.html 前提条件 已安装 Kuboard v3,版 ...
- frpc穿透报错 日志显示 http: proxy error: no such domain 解决办法
问题出在客户端的设置上,比如你的frps服务器IP为114.114.114.114,设置的vhost_http_port端口为 8080,在客户端设置的是域名fk.abc.com 指向frps所在服务 ...
- 单台主机MySQL多实例部署
二进制安装mysql-5.7.26 [root@mysql ~]# cd /server/tools/ [root@mysql tools]# ll total 629756 -rw-r--r-- 1 ...
- ERP是什么呢?
ERP(Enterprise Resource Planning,企业资源计划)系统,是进行物质资源.资金资源和信息资源集成一体化管理的企业信息管理系统,ERP统领企业全局,为管理层服务,重心在于企业 ...
- 关于aws-SecurityGroup-安全组策略的批量添加的方法记录
因一些服务的客户端网络地址段计划变更,会影响到aws上配置这这些网段安全组策略所绑定的资源 因此需要先整理包含了出那些服务的网络地址段的安全组 然后根据旧网段的策略信息,将新的地址段给添加上,待后续正 ...
- CSP-J2020 洛谷P7072 直播获奖(Splay/桶排序)
题目描述 NOI2130 即将举行.为了增加观赏性,CCF 决定逐一评出每个选手的成绩,并直播即时的获奖分数线.本次竞赛的获奖率为 w%,即当前排名前 w% 的选手的最低成绩就是即时的分数线. 更具体 ...