抽象,特别是面向接口的抽象,可以使程序变得更加通用。本文通过对 io 库设计的分析,介绍使用一些相关的包,如strings,bytes等,处理普通文件、内存块、字符串、管道等流式应用,以及常用文本和二进制流的读写,帮助读者理解抽象设计的重要性。
文章目录
本以为 go 语言 io 处理很容易,结果发现网上有效资源特别少,并没有系统讲述 go 语言文本流、二进制流处理的文档。于是写了很多,但绝对很实用!
本文要点:
- io 库的设计与抽象
- 装饰模式(Decorator pattern)
- 文本流与二进制流的读写
1、 io.go 源代码与 IO流抽象
阅读 io 库文档 真不如阅读 io.go 源代码,因为源代码是按作者思考逻辑组织的,而参考文档是按固定结构、字典顺序组织的。
1.1 io 包的职责
包设计的基本原则是职责内聚,包设计职责描述是判断设计合理性的重要方面。它通常可以用几句化描述,就 IO 包而言:
- 提供 io流操作原语(I/O primitives) 的基本接口。这些接口为实现 io 流处理的包,如 os 等包在实现流操作时,提供统一的抽象。
- 提供一些实用函数,方便各种IO流的处理
- 除非特别声明,这些原语实现不能假设为线程安全
1.2 IO 操作原语定义与基础接口
在源代码中,对于 IO 流,定义了四个基本操作原语,分别用 Reader,Writer,Closer,Seeker 接口表达二进制流读、写、关闭、寻址操作。 源代码:
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
type Closer interface {
Close() error
}
type Seeker interface {
Seek(offset int64, whence int) (int64, error)
}
然后,定义了一些原语组合,表达一些常用的流文件的处理能力。如只读、只写、可读写流。 源代码:
// ReadWriter is the interface that groups the basic Read and Write methods.
type ReadWriter interface {
Reader
Writer
}
type ReadCloser interface {
...}
type WriteCloser interface {
...}
type ReadWriteCloser interface {
...}
type ReadSeeker interface {
...}
type WriteSeeker interface {
...}
type ReadWriteSeeker interface {
...}
为了兼容以往流编程习惯,IO 库定义了一些常见的基本操作,例如,读写一个字符,seek 到特定位置读写,字串的读写等,源代码:
type ReaderAt interface {
ReadAt(p []byte, off int64) (n int, err error)
}
type WriterAt interface {
...}
type ByteReader interface {
ReadByte() (byte, error)
}
type ByteWriter interface {
...}
type RuneReader interface {
ReadRune() (r rune, size int, err error)
}
// stringWriter is the interface that wraps the WriteString method.
type stringWriter interface {
WriteString(s string) (n int, err error)
}
这里有一些细节:
- 为社么一些接口命名方法不统一,如:ReadAt?
- 内部接口
stringWriter
有什么用? - 是否需要更多的定义,如 IntReader?
1.3 实用 IO 处理函数
在满足上述原语定义的流,就有许多常见的实用功能。提供这些函数不仅方便用户使用该 IO 库,同时也展现了抽象带来的应用价值。
例如:复制流(文件copy),源代码:
func Copy(dst Writer, src Reader)