Go 设计哲学:简单、显式、组合、并发和面向工程
简单:
仅有 25 个关键字,主流编程语言最少;
内置垃圾收集,降低开发人员内存管理的心智负担;
首字母大小写决定可见性,无需通过额外关键字修饰;
变量初始为类型零值,避免以随机值作为初值的问题;
内置数组边界检查,极大减少越界访问带来的安全隐患;
内置并发支持,简化并发程序设计;
内置接口类型,为组合的设计哲学奠定基础;
原生提供完善的工具链,开箱即用;
组合:
Go 语言无类型层次体系,各类型之间是相互独立的,没有子类型的概念;每个类型都可以有自己的方法集合,类型定义与方法实现是正交独立的;实现某个接口时,无需像 Java 那样采用特定关键字修饰;包之间是相对独立的,没有子包的概念。
示例1:
// $GOROOT/src/sync/pool.go
type poolLocal struct {
private interface{}
shared []interface{}
Mutex
pad [128]byte
}
Go 标准库的一个代码段,在 poolLocal 这个结构体类型中嵌入了类型 Mutex,这就使得 poolLocal 这个类型具有了互斥同步的能力,我们可以通过 poolLocal 类型的变量,直接调用 Mutex 类型的方法 Lock 或 Unlock;
示例2:
// $GOROOT/src/io/io.go
type ReadWriter interface {
Reader
Writer
}
标准库通过嵌入接口类型的方式来实现接口行为的聚合,组成大接口;
示例3:
// $GOROOT/src/io/ioutil/ioutil.go
func ReadAll(r io.Reader)([]byte, error)
// $GOROOT/src/io/io.go
func Copy(dst Writer, src Reader)(written int64, err error)
函数 ReadAll 通过 io.Reader 这个接口,将 io.Reader 的实现与 ReadAll 所在的包低耦合地水平组合在一起了,从而达到从任意实现 io.Reader 的数据源读取所有数据的目的。类似的水平组合“模式”还有点缀器、中间件等;
并发:
Go 运行时默认为每个 goroutine 分配的栈空间仅 2KB;
Go 还在语言层面内置了辅助并发设计的原语:channel 和 select。开发者可以通过语言内置的 channel 传递消息或实现同步,并通过 select 实现多路 channel 的并发控制。
面向工程
重新设计编译单元和目标文件格式,实现 Go 源码快速构建,让大工程的构建时间缩短到类似动态语言的交互式解释的编译速度;如果源文件导入它不使用的包,则程序将无法编译。这可以充分保证任何 Go 程序的依赖树是精确的。这也可以保证在构建程序时不会编译额外的代码,从而最大限度地缩短编译时间;去除包的循环依赖,循环依赖会在大规模的代码中引发问题,因为它们要求编译器同时处理更大的源文件集,这会减慢增量构建;包路径是唯一的,而包名不必唯一的。导入路径必须唯一标识要导入的包,而名称只是包的使用者如何引用其内容的约定。“包名称不必是唯一的”这个约定,大大降低了开发人员给包起唯一名字的心智负担;故意不支持默认函数参数。因为在规模工程中,很多开发者利用默认函数参数机制,向函数添加过多的参数以弥补函数 API 的设计缺陷,这会导致函数拥有太多的参数,降低清晰度和可读性;增加类型别名(type alias),支持大规模代码库的重构。
Go 被称为“自带电池”的编程语言,Go 在标准库中提供了各类高质量且性能优良的功能包,其中的net/http、crypto、encoding等包充分迎合了云原生时代的关于 API/RPC Web 服务的构建需求,Go 开发者可以直接基于标准库提供的这些包实现一个满足生产要求的 API 服务,从而减少对外部第三方包或库的依赖,降低工程代码依赖管理的复杂性