第二条原则: 偏好组合,正交解耦
在 Go 语言设计层面,Go 设计者为 gopher 们提供了正交的语法元素供后续组合使用,包括:
- Go 语言无类型体系(type hierarchy),类型之间是独立的,没有子类型的概念;
- 每个类型都可以有自己的方法集合,类型定义与方法实现是正交独立的;
- 接口(interface)与其实现之间”隐式关联”;
- 包(package)之间是相对独立的,没有子包的概念。
Go 语言提供了的最为直观的组合的语法元素就是type embedding,即类型嵌入:
- 通过类型嵌入,我们可以将已经实现的功能嵌入到新类型中,以快速满足新类型的功能需求,这种方式有些类似经典 OO 的“继承”,但在原理上与经典 OO 的继承完全不同。
- 这是一种 Go 精心设计的“语法糖”,被嵌入的类型和新类型两者之间没有任何关系,甚至相互完全不知道对方的存在,更没有经典 OO 那种父类、子类的关系以及向上、向下转型(type casting)。
- 通过新类型实例调用方法时,方法的匹配取决于方法名字,而不是类型。这种组合方式,我称之为“垂直组合”,即通过类型嵌入,快速让一个新类型“复用”其他类型已经实现的能力,实现功能的垂直扩展。
类型嵌入的例子:
- 我们在 poolLocal 这个 struct 中嵌入类型 Mutex,被嵌入的 Mutex 类型的方法集合会被提升到外面的类型中。比如,这里的 poolLocal 将拥有 Mutex 类型的 Lock 和 Unlock 方法。实际调用时,方法调用实际会被传给 poolLocal 中的 Mutex 实例。
// Go标准库:sync/pool.gotype poolLocal struct {private interface{} // Can be used only by the respective P.shared []interface{} // Can be used by any P.Mutex // Protects shared.pad [128]byte // Prevents false sharing.}
interface 类型嵌入的代码:
- 通过在 interface 中嵌入 interface type,实现接口行为的聚合,组成大接口,这种方式在标准库中尤为常用,并且已经成为了 Go 语言的一种常见的惯用法。
type ReadWriter interface {ReaderWriter}
interface 是 Go 语言中真正的魔法,是 Go 语言的一个创新设计:
- 它只是方法集合,并且它与实现者之间的关系是隐式的,它让程序内部各部分之间的耦合降至最低
- 同时它也是连接程序各个部分之间“纽带”。
- 隐式的 interface 实现会不经意间满足:依赖抽象、里氏替换、接口隔离等原则,这在其他语言中是需要很”刻意”的设计谋划才能实现的,但在 Go interface 来看,一切却是自然而然的。
通过 interface 将程序内部各个部分组合在一起的方法,我这里称之为水平组合。水平组合的“模式”很多,比如:一种常见方法就是:通过接受 interface 类型参数的普通函数进行组合,例如下面代码。
func ReadAll(r io.Reader) ([]byte, error)func Copy(dst Writer, src Reader) (written int64, err error)
