第二部分导读:抽象与组织
程序变大以后,总会碰到同样几件事:一段逻辑在三个地方用到,不想复制三份;几种东西要以同一种方式被调用,各自的细节又不同;某一步可能失败,调用方得知道;文件多了,哪些名字给外面用,哪些只在里面用。这一部分讲的共同问题是:怎么把代码分块、复用、隔开。
四种语言给出的工具不同。比如“几种东西共用一套用法”:Python 允许多重继承,也接受只要有对应方法就行的鸭子类型;C# 是单继承加多个接口,而且必须在类型上声明实现了哪个接口;Go 没有类和继承,方法对上了就算实现接口,共享行为靠嵌入。再比如“同一段逻辑用在不同类型上”:C# 的泛型在运行时仍然存在,TS 和 Python 的泛型只给检查器看,Go 1.18 才加入,JS 则没有。
这一部分建立在第一部分之上。四种语言的闭包都按变量捕获,而变量怎样指向值,见第 4、5 章讲的名字与引用;泛型能不能在运行时知道类型参数,取决于第 3 章讲的类型信息保不保留;错误怎样用 None、nil 之类的值表示,也回到第 6 章。读不顺时可以回头查。
| 章 | 一句话 | 四种语言差异最大的一点 |
|---|---|---|
| 第 7 章 函数:一等函数、闭包、参数、多返回值 | 把一段计算当成值,记住创建时的环境 | 默认参数求值时机:同样写 = 20,Python 在定义时求一次,JS 每次调用时求,C# 在编译期烧进调用方,Go 没有默认参数 |
| 第 8 章 类、接口与组合 | 多种实现共用一套用法,并找到该执行的代码 | 方法分派的实现机制:Python 和 JS 在运行时按名字找方法,靠缓存提速,C# 和 Go 按位置从 vtable 或 itab 取函数,C# 的接口调用另经存根缓存 |
| 第 9 章 泛型 | 同一段逻辑用在不同类型上,不丢类型检查 | 运行时能否知道类型参数:Python 和 TS 不能,C# 和 Go 能,决定了能不能按 T 分支、能不能反射;JS 没有泛型 |
| 第 10 章 错误处理与资源清理 | 做不到时告诉调用方,并保证资源被关上 | 错误的传递:Python、JS/TS、C# 抛异常,Go 返回错误值,清理分别靠 with、finally、using、defer |
| 第 11 章 模块、包、可见性与依赖管理 | 把代码切开,决定哪些名字对外,并固定依赖版本 | 可见性规则:Python 只写给人看,JS/TS 和 Go 按文件或包挡住,C# 挡在程序集这一层 |
下面这张图列出这部分的五类问题,以及各自该翻的章。
图的左列是你可能遇到的问题,右列是该先翻的章。
遇到什么问题先看哪章:
- 你想把一段计算存起来或传给别人,或者想让一个函数返回多个结果 → 看第 7 章 函数。
- 你想让几种东西共用一套调用方式,又各自实现 → 看第 8 章 类、接口与组合。
- 你想写一个容器或工具函数,让它适用于多种类型 → 看第 9 章 泛型。
- 你想让调用方知道哪里出了错,并保证文件、连接等资源被关上 → 看第 10 章 错误处理与资源清理。
- 你想把代码拆成多个文件,限定哪些名字能被外面用,并固定第三方依赖的版本 → 看第 11 章 模块、包、可见性与依赖管理。