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 语言自带标准库中一些比较核心的代码包.这会涉及这些代码包的标准用法.使用禁忌.背后原理以及周边的知识. ...
随机推荐
- KingbaseES 全局临时表
Postgresql 支持会话级别的临时表,表的存续期只在创建临时表的会话存活期间,会话退出后,临时表自动删除,表结构及数据也无法跨会话共享.KingbaseES 除了支持PG原生的临时表机制外,还支 ...
- 使用verdaccio+docker搭建npm私有仓库以及使用
公司内部前端组件或库的共享等,搭建一个npm私有库就很方便,现在中大型公司也基本都有自己的npm私有库,这篇文章,和大家一起共同搭建一个npm私有库,共同学习 前置条件 一台电脑 可以联网 一.安装d ...
- URL slug是什么?
URL slug是什么? URL slug是 URL 中最后一个反斜杠之后的部分. 为什么slug 对 SEO 很重要? slug 对于关键字 SEO 来说非常强大.如果用户要在 Google 搜索& ...
- 第六章:Django 综合篇 - 16:Authentication
Django自带一个用户认证系统,用于处理用户账户.群组.许可和基于cookie的用户会话. Django的认证系统包含了身份验证和权限管理两部分.简单地说,身份验证用于核实某个用户是否合法,权限管理 ...
- ProxySQL SQL 注入引擎
ProxySQL 2.0.9 引入了 libsqlinjection 作为识别可能的 SQL 注入攻击的机制. 启用 SQL 注入检测 要启用 SQL 注入检测,只需要启用变量 mysql-aut ...
- MySQL读写分离之——ProxySQL
文章转载自:https://blog.csdn.net/u012280685/article/details/113520692?spm=1001.2014.3001.5501 实现一个简单的读写分离 ...
- Shell分析日志文件
文章转载自:https://mp.weixin.qq.com/s/o63aIM2p9rc2OjhxiC6wgA 1.查看有多少个IP访问: awk '{print $1}' log_file|sort ...
- 使用 Skywalking Agent,这里使用sidecar 模式挂载 agent
文章转载自:https://bbs.huaweicloud.com/blogs/315037 方法汇总 Java 中使用 agent ,提供了以下三种方式供你选择 使用官方提供的基础镜像 将 agen ...
- linux软链接的创建、修改和删除
创建 ln -s [源文件或目录] [目标文件或目录] 修改 ln –snf [新的源文件或目录] [目标文件或目录] 删除 rm –rf 软链接名称 注意,上面这种形式可能会让人产生担忧,害怕删除的 ...
- Pod的dns记录怎么组成的
Pod的dns记录怎么组成的 <Pod Name>.<service name>.<namespace name>.svc.cluster.local Pod的Na ...