---Tony Bai · Go语言第一课

设计哲学之于编程语言,就好比一个人的价值观之于这个人的行为。

Go 语言的设计哲学总结为五点:简单、显式、组合、并发和面向工程。

简单

Go 语法层面:

  1. 仅有 25 个关键字,主流编程语言最少
  2. 内置垃圾收集,降低开发人员内存管理的心智负担;
  3. 首字母大小写决定可见性,无需通过额外关键字修饰;
  4. 变量初始为类型零值,避免以随机值作为初值的问题;
  5. 内置数组边界检查,极大减少越界访问带来的安全隐患;
  6. 内置并发支持,简化并发程序设计;
  7. 内置接口类型,为组合的设计哲学奠定基础;
  8. 原生提供完善的工具链,开箱即用;

简单的设计哲学是 Go 生产力的源泉.

显式

package main
import "fmt"
func main() {
var a int16 = 5
var b int = 8
var c int64
c = a + b
fmt.Printf("%d\n", c)
}

如果我们编译这段程序,将得到类似这样的编译器错误:“invalid operation: a + b
(mismatched types int16 and int)”。我们能看到 Go 与 C 语言的隐式自动类型转换不
同,Go 不允许不同类型的整型变量进行混合计算,它同样也不会对其进行隐式的自动转
换。

在 Go 语言中,不同类型变量是不能在一起进行混合计算的,这是因为 Go 希望开发人员
明确知道自己在做什么.

组合

Go 推崇的是组合的设计哲学.

  1. Go 语言无类型层次体系,各类型之间是相互独立的,没有子类型的概念;
  2. 每个类型都可以有自己的方法集合,类型定义与方法实现是正交独立的;
  3. 实现某个接口时,无需像 Java 那样采用特定关键字修饰;
  4. 包之间是相对独立的,没有子包的概念。

Go 语言其实是为我们呈现了这样的一幅图景:一座座没有关联的“孤岛”,但每个岛内又都很精彩。那么现在摆在面前的工作,就是在这些孤岛之间以最适当的方式建立关联,并形成一个整体。而 Go 选择采用的组合方式,也是最主要的方式.

Go 语言为支撑组合的设计提供了类型嵌入(Type Embedding)。通过类型嵌入,我们可
以将已经实现的功能嵌入到新类型中,以快速满足新类型的功能需求.

被嵌入的类型和新类型两者之间没有任何关系,甚至相互完全不知道对方的存在,更没有
经典面向对象语言中的那种父类、子类的关系,以及向上、向下转型(Type Casting)。
通过新类型实例调用方法时,方法的匹配主要取决于方法名字,而不是类型。这种组合方
式,我称之为垂直组合,即通过类型嵌入,快速让一个新类型“复用”其他类型已经实现
的能力,实现功能的垂直扩展。

你可以看看下面这个 Go 标准库中的一段使用类型嵌入的组合方式的代码段。

// $GOROOT/src/sync/pool.go
type poolLocal struct {
   private interface{}
    shared []interface{}
    Mutex
    pad [128]byte
}

在代码段中,我们在 poolLocal 这个结构体类型中嵌入了类型 Mutex,这就使得
poolLocal 这个类型具有了互斥同步的能力,我们可以通过 poolLocal 类型的变量,直接
调用 Mutex 类型的方法 Lock 或 Unlock。

我们在标准库中还会经常看到类似如下定义接口类型的代码段:

type ReadWriter interface {
   Reader
   Writer
}

这里,标准库通过嵌入接口类型的方式来实现接口行为的聚合,组成大接口,这种方式在
标准库中尤为常用,并且已经成为了 Go 语言的一种惯用法。

垂直组合本质上是一种“能力继承”,采用嵌入方式定义的新类型继承了嵌入类型的能
力。Go 还有一种常见的组合方式,叫水平组合。和垂直组合的能力继承不同,水平组合是
一种能力委托(Delegate),我们通常使用接口类型来实现水平组合。

比如一种常见方法就是,通过接受接口类型参数的普通函数进行
组合,如以下代码段所示:

// $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 放弃了传统的基于操作系统线程的并发模型,而采用了用户层轻量级线程,Go 将之称为 goroutine。

goroutine 占用的资源非常小,Go 运行时默认为每个 goroutine 分配的栈空间仅 2KB。
goroutine 调度的切换也不用陷入(trap)操作系统内核层完成,代价很低。因此,一个
Go 程序中可以创建成千上万个并发的 goroutine。而且,所有的 Go 代码都在 goroutine
中执行,哪怕是 go 运行时的代码也不例外。

面向工程

Go 语言设计的初衷,就是面向解决真实世界中 Google 内部大规模软件开发存在的各种
问题,为这些问题提供答案,这些问题包括:程序构建慢、依赖管理失控、代码难于理
解、跨语言构建难等。


goper
413 声望25 粉丝

go 后端开发