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 语言自带标准库中一些比较核心的代码包.这会涉及这些代码包的标准用法.使用禁忌.背后原理以及周边的知识. ...
随机推荐
- 虚拟机里做LUN映射(RHEL系统和centos系统皆可)(Linux版)
紧接着Windows的LUN映射之后 参考 (https://www.cnblogs.com/zhengyan6/p/16121268.html) 先删除部分配置(没有做之前的LUN映射则不用) 进网 ...
- KingbaseES 匿名块如何传递参数
匿名块的基本语法结构包括声明和执行两部分.匿名块每次提交都被重新编译和执行.因为匿名块没有名称并不在数据库中存储,所以匿名块不能直接从其他PL/SQL 块中调用. 定义语法: [ DECLARE ] ...
- 大家都能看得懂的源码之 ahooks useVirtualList 封装虚拟滚动列表
本文是深入浅出 ahooks 源码系列文章的第十八篇,该系列已整理成文档-地址.觉得还不错,给个 star 支持一下哈,Thanks. 简介 提供虚拟化列表能力的 Hook,用于解决展示海量数据渲染时 ...
- 跟羽夏学 Ghidra ——引用
写在前面 此系列是本人一个字一个字码出来的,包括示例和实验截图.本人非计算机专业,可能对本教程涉及的事物没有了解的足够深入,如有错误,欢迎批评指正. 如有好的建议,欢迎反馈.码字不易,如果本篇文章 ...
- 我的Vue之旅、01 深入Flexbox布局完全指南
花了几个小时整合的"A Complete Guide to Flexbox"最新版本,介绍了flexbox的所有属性,外带几个实用的例子. 传统布局.Flexbox 布局的传统解决 ...
- 《网页设计基础——CSS常用语法》
网页设计基础--CSS常用语法 一.注释: 例如: /* 在此处书写注释 */ 二.清除浏览器默认设置: 例如: *{ /* 全局声明 */ margin: 0; padding: ...
- KeeWiDB:兼容Redis协议,领跑NoSQL
如果现在的我们离开了互联网,生活会是什么样子? 互联网++++,已经深刻渗透到人们的生活中. 不知道大家有没有想过?每一个互联网+结合的背后都是海量的存储需求.你查看的每一个商品.组建的每一个战队.阅 ...
- Elasticsearch准实时索引实现(数据写入到es分片并存储到文件中的过程)
溢写到文件系统缓存 当数据写入到ES分片时,会首先写入到内存中,然后通过内存的buffer生成一个segment,并刷到文件系统缓存中,数据可以被检索(注意不是直接刷到磁盘) ES中默认1秒,refr ...
- 跟我学Python图像处理丨带你掌握傅里叶变换原理及实现
摘要:傅里叶变换主要是将时间域上的信号转变为频率域上的信号,用来进行图像除噪.图像增强等处理. 本文分享自华为云社区<[Python图像处理] 二十二.Python图像傅里叶变换原理及实现> ...
- Ant Design槽位失效
保证数据结构中有scopedSlots: { title: 'title' }, 即包含scopedSlots属性 使用时名字应保证一致 例如: 数据结构: treeData: [ { key ...