如果说C++和Java是关于类型层次结构和类型分类的语言,那么Go则是关于组合的语言。 —— Rob Pike,Go语言之父
“偏好组合,正交解耦”
29.1 一切皆组合
在语言设计层面,Go提供了诸多正交的语法元素供后续组合使用,包括:
- Go语言无类型体系(type hierarchy),类型定义独立;
- 方法和类型是正交的,每种类型都可以拥有自己的方法集合;
- 接口与其实现者之间无显式关联。
主要的两种组合方式:
- 垂直组合(类型组合):通过类型嵌入机制实现垂直组合,进而实现方法实现的复用、接口定义重用
- 水平组合:以接口类型变量作为程序水平组合的连接点
29.2 垂直组合回顾
Go语言通过类型的垂直组合而不是继承让单一类型承载更多的功能。调用方法时,方法的匹配取决于方法名称,而不是类型。
(1) 通过嵌入接口构建接口
// $GOROOT/src/io/io.go
type ReadWriter interface {
Reader
Writer
}
(2) 通过嵌入接口构建结构体
type MyReader struct {
io.Reader // 底层的reader
N int64 // 剩余最大字节数
}
嵌入io.Reader的MyReader类型自然实现了io.Reader接口。可以用于快速构建满足某一接口的结构体类型,以满足单元测试的需要,而我们仅需实现少数需要的接口方法即可
(3) 通过嵌入结构体构建新结构体
// $GOROOT/src/sync/pool.go
type poolLocal struct {
private interface{}
shared []interface{}
Mutex
pad [128]byte
}
被嵌入Mutex的poolLocal“继承”了Mutex的Lock和Unlock实现,但实质上在结构体中嵌入接口类型名和在结构体中嵌入结构体,都是“委派模式”(delegate)的一种应用。
对新结构体类型的方法调用可能会被“委派”给该结构体内部嵌入的结构体的实例。
比如上面的poolLocal结构体,对于外部来说它拥有Lock和Unlock方法,但当Lock/Unlock方法被调用时,方法调用实际被传给了poolLocal中的Mutex实例。
29.3 以接口为连接点的水平组合
以接口为连接点的水平组合方式可以将各个垂直组合出的类型耦合在一起,从而编织出程序静态骨架。而通过接口进行水平组合的一种常见模式是使用接受接口类型参数的函数或方法。
以下是以接口为连接点的水平组合的几种惯用形式。
1.基本形式
水平组合的基本形式是接受接口类型参数的函数或方法,代码如下:
func YourFuncName(param YourInterfaceType)
函数/方法参数中的接口类型作为连接点,将位于多个包中的多个类型“编织”到一起,共同形成一幅程序“骨架”。
同时接口类型与其实现者之间隐式的关系在不经意间满足了依赖抽象、里氏替换原则、接口隔离等代码设计原则。
实例:
// $GOROOT/src/io/ioutil/ioutil.go
func readAll(r io.Reader, capacity int64) (b []byte, err error)
// $GOROOT/src/io/io.go
func Copy(dst Writer, src Reader) (written int64, err error)
2.包裹函数
包裹函数(wrapper function)的形式是这样的:它接受接口类型参数,并返回与其参数类型相同的返回值。其代码如下:
func YourWrapperFunc (param YourInterfaceType) YourInterfaceType
通过包裹函数可以实现对输入数据的过滤、装饰、变换等操作,并将结果再次返回给调用者。
// $GOROOT/src/io/io.go
func LimitReader(r Reader, n int64) Reader { return &LimitedReader{r, n}}
type LimitedReader struct {
R Reader
N int64
}
func (l *LimitedReader) Read(p []byte) (n int, err error) {
...
}
由于包裹函数的返回值类型与参数类型相同,因此我们可以将多个接受同一接口类型参数的包裹函数组合成一条链来调用,其形式如下:
YourWrapperFunc1(YourWrapperFunc2(YourWrapperFunc3(...)))
3.适配器函数类型
适配器函数类型(adapter function type)是一个辅助水平组合实现的“工具”类型。强调一下,它是一个类型。
它可以将一个满足特定函数签名的普通函数显式转换成自身类型的实例,转换后的实例同时也是某个单方法接口类型的实现者。
最典型的适配器函数类型莫过于第21条提到过的http.HandlerFunc了。
// $GOROOT/src/net/http/server.go
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
4.中间件
在Go Web编程中,“中间件”常常指的是一个实现了http.HandlerFunc类型实例,如authHandler、loginHandler。实质上,这里的中间件就是包裹函数和适配器函数类型结合的产物。
标签:组合,读书笔记,29,接口,类型,接口类型,io,Reader,Go From: https://www.cnblogs.com/brynchen/p/18022044