Go语言精进之路:从新手到高手的编程思想、方法和技巧 1(310365)

0评论量

10阅读量

内容简介作者简介编辑推荐精彩书摘目录
Go入门容易,精进难,如何才能像Go开发团队那样写出符合Go思维和语言惯例的高质量代码呢? 本书将从编程思维和实践技巧2个维度给出答案,帮助你在Go进阶的路上事半功倍。 编程思维层面 只有真正领悟了一门语言的设计哲学和编程思维,并能将之用于实践,才算精通了这门语言。本书从Go语言设计者的视角对Go背后的设计哲学和编程思想进行了梳理和分析,指引读者体会那些看似随意实则经过深思熟虑的设计背后的秘密。 实践技巧层面 实践技巧源于对Go开发团队和Go社区开发的高质量代码的阅读、挖掘和归纳,从项目结构、代码风格、语法及其实现、接口、并发、同步、错误与异常处理、测试与调试、性能优化、标准库、第三方库、工具链、Z佳实践、工程实践等多个方面给出了改善Go代码质量、写出符合 Go 思维和惯例的代码的有效实践。 全书的内容覆盖如下10个大类,共66个主题,字字珠玑,句句箴言。 (1)Go语言的一切 (2)项目结构、代码风格与标识符命名 (3)声明、类型、语句与控制结构 (4)函数与方法 (5)接口 (6)并发编程 (7)错误处理 (8)测试、性能剖析与调试 (9)标准库、反射与cgo (10)工具链与工程实践 学完这本书,你将拥有和 Go专家一样的编程思维,写出符合Go惯例和风格的高质量代码,从众多 Go 初学者中脱颖而出,快速实现从Go新手到专家的转变!
白明(Tony Bai) 资深Go技术专家和架构师,有超过10年的服务端架构设计和开发经验,擅长服务器后端编程。曾任东软集团开发部技术总监和东网科技高级架构师,现为东软睿驰智能网联汽车业务线精通车联网平台的高级架构师。精通Go、C、Python、Shell、Linux、Rancher、Docker、OpenStack、Kubernetes等技术栈。 《七周七语言》译者之一,GopherChina大会、开源中国源创会讲师,麦思博客座培训师,tonybai.com技术博客博主。
(1)作者经验丰富:资深Go技术专家和架构师,曾任东软集团开发部技术总监,现为东软睿驰智能网联汽车业务线车联网平台架构师。 (2)经典精华提炼:内容基于Go标准库、第三方库、Go项目等高质量Go代码,分析并提炼其中编程实践精华。 (3)3大社区力荐:GoCN社区、GopherChina、Go语言中文网3大社区联袂推荐。 (4)语言设计哲学:从Go语言设计者的角度,透析Go语言设计的哲学,揭开那些看似随意实为精心设计的秘密。 (5)Go编程思想:不掌握Go语言编程思想不足以说明精通Go语言编程,本书会无声息地将编程思想融入你的日常编码。 (6)66个主题:从项目结构、代码风格、语法及其实现、接口、并发、同步、错误与异常处理、测试与调试、性能优化、标准库、第三方库、工具链、*佳实践、工程实践等方面总结能帮助初学者快速进阶的66个主题。
前言 为什么要写本书 Go是Google三位大师级人物Robert Griesemer、Rob Pike及Ken Thompson共同设计的一种静态类型、编译型编程语言。它于2009年11月正式开源,一经面世就凭借语法简单、原生支持并发、标准库强大、工具链丰富等优点吸引了大量开发者。经过十余年演进和发展,Go如今已成为主流云原生编程语言,很多云原生时代的杀手级平台、中间件、协议和应用都是采用Go语言开发的,比如Docker、Kubernetes、以太坊、Hyperledger Fabric超级账本、新一代互联网基础设施协议IPFS等。 Go是一门特别容易入门的编程语言,无论是刚出校门的新手还是从其他编程语言转过来的老手,都可以在短时间内快速掌握Go语法并编写Go代码。但很多Go初学者的疑问是:Go入门容易,但精进难,怎么才能像Go开发团队那样写出符合Go思维和语言惯例的高质量代码呢?这个问题引发了我的思考。在2017年GopherChina大会上,我以演讲的形式初次尝试回答这个问题,但鉴于演讲的时长有限,很多内容没能展开,效果不甚理想。而本书正是我对解答这个问题所做出的第二次尝试。 我这次解答的思路有两个。 ■思维层面:写出高质量Go代码的前提是思维方式的进阶,即用Go语言的思维写Go代码。 ■实践技巧层面:Go标准库和优秀Go开源库是挖掘符合Go惯用法的高质量Go代码的宝库,对其进行阅读、整理和归纳,可以得到一些能够帮助我们快速进阶的有效实践。 本书正是基于以上思路为想实现Go精进但又不知从何入手的你而写的。 首届图灵奖得主、著名计算机科学家Alan J. Perlis曾说过:“不能影响到你的编程思维方式的编程语言不值得学习和使用。”由此可见编程思维对编程语言学习和应用的重要性。只有真正领悟了一门编程语言的设计哲学和编程思维,并将其应用到日常编程当中,你才算真正精通了这门编程语言。 因此,本书将首先带领大家回顾Go语言的演进历程,一起了解Go语言设计者在设计Go语言时的所思所想,与他们产生思维上的共鸣,深刻体会那些看似随意实则经过深思熟虑的设计。 接下来,本书将基于对Go开发团队、Go社区高质量代码的分析与归纳,从项目结构和代码风格、基础语法、函数、方法、接口、并发、错误处理、测试与性能优化、标准库、工具链等多个方面,给出改善Go代码质量、写出符合Go思维和惯例的代码的箴言。 学习了本书中的这些箴言,你将拥有和Go专家一样的Go编程思维,写出符合Go惯例风格的高质量Go代码,从众多Go初学者中脱颖而出,快速实现从Go编程新手到专家的转变! 读者对象 本书主要适合以下人员阅读: ■迫切希望在Go语言上精进并上升到新层次的Go语言初学者; ■希望写出更符合Go惯用法的高质量代码的Go语言开发者; ■有Go语言面试需求的在校生或Go语言求职者; ■已掌握其他编程语言且希望深入学习Go语言的开发者。 本书特色 本书的特色可以概括为以下几点。 ■进阶必备:精心总结的编程箴言助你掌握高效Go程序设计之道。 ■高屋建瓴:Go设计哲学与编程思想先行。 ■深入浅出:原理深入,例子简明,讲解透彻。 ■图文并茂:大量图表辅助学习,重点、难点轻松掌控。 如何阅读本书 本书内容共分为十部分,限于篇幅,分为两册出版,即《Go语言精进之路:从新手到高手的编程思想、方法和技巧1》和《Go语言精进之路:从新手到高手的编程思想、方法和技巧2》。其中,第1册包含第一~七部分,第2册包含第八~十部分。 ■第一部分 熟知Go语言的一切 本部分将带领读者穿越时空,回顾历史,详细了解Go语言的诞生、演进以及发展现状。通过归纳总结Go语言的设计哲学和原生编程思维,让读者站在语言设计者的高度理解Go语言与众不同的设计,认同Go语言的设计理念。 ■第二部分 项目结构、代码风格与标识符命名 每种编程语言都有自己惯用的代码风格,而遵循语言惯用风格是编写高质量Go代码的必要条件。本部分详细介绍了得到公认且广泛使用的Go项目的结构布局、代码风格标准、标识符命名惯例等。 ■第三部分 声明、类型、语句与控制结构 本部分详述基础语法层面高质量Go代码的惯用法和有效实践,涵盖无类型常量的作用、定义Go的枚举常量、零值可用类型的意义、切片原理以及高效的原因、Go包导入路径的真正含义等。 ■第四部分 函数与方法 函数和方法是Go程序的基本组成单元。本部分聚焦于函数与方法的设计和实现,涵盖init函数的使用、跻身“一等公民”行列的函数有何不同、Go方法的本质等。 ■第五部分 接口 接口是Go语言中的“魔法师”。本部分聚焦于接口,涵盖接口的设计惯例、使用接口类型的注意事项以及接口类型对代码可测试性的影响等。 ■第六部分 并发编程 Go以其轻量级的并发模型而闻名。本部分详细介绍Go基本执行单元——goroutine的调度原理、Go并发模型以及常见并发模式、Go支持并发的原生类型——channel的惯用模式等内容。 ■第七部分 错误处理 Go语言十分重视错误处理,它有着相对保守的设计和显式处理错误的惯例。本部分涵盖Go错误处理的哲学以及在这套哲学下一些常见错误处理问题的优秀实践。 ■第八部分 测试、性能剖析与调试 Go自带强大且为人所称道的工具链。本部分详细介绍Go在单元测试、性能基准测试与性能剖析以及代码调试方面的最佳实践。 ■第九部分 标准库、反射与cgo Go拥有功能强大且质量上乘的标准库,在多数情况下仅使用标准库即可实现应用的大部分功能,这大幅降低了学习成本以及代码依赖的管理成本。本部分详细说明高频使用的标准库包(如net/http、strings、bytes、time等)的正确使用方式,以及在使用reflect包、cgo时的注意事项。 ■第十部分 工具链与工程实践 本部分涵盖在使用Go语言进行大型软件项目开发的过程中,我们很有可能会遇到的一些工程问题的解决方法,包括使用go module进行Go包依赖管理、Go程序容器镜像、Go相关工具使用以及Go语言的避“坑”指南。 勘误和支持 由于作者水平有限,写作时间仓促,以及技术的不断更新和迭代,书中难免会存在一些错误或者不准确的地方,恳请读者批评指正。书中的源文件可以从https://github.com/bigwhite/GoProgrammingFromBeginnerToMaster下载。如果你有更多的宝贵意见,欢迎发送邮件至邮箱bigwhite.cn@aliyun.com,期待你的真挚反馈。 致谢 感谢机械工业出版社华章公司的编辑杨福川与罗词亮,在这一年多的时间里,他们的支持与鼓励让我顺利完成全部书稿。 谨以此书献给Go语言社区的关注者和建设者! 白明 2021年12月
第一部分 熟知Go语言的一切 第1条 了解Go语言的诞生与演进2 1.1 Go语言的诞生2 1.2 Go语言的早期团队和演进历程4 1.3 Go语言正式发布并开源4 第2条 选择适当的Go语言版本6 2.1 Go语言的先祖6 2.2 Go语言的版本发布历史7 2.3 Go语言的版本选择建议11 第3条 理解Go语言的设计哲学12 3.1 追求简单,少即是多12 3.2 偏好组合,正交解耦15 3.3 原生并发,轻量高效17 3.4 面向工程,“自带电池”21 第4条 使用Go语言原生编程思维来写Go代码26 4.1 语言与思维—来自大师的观点26 4.2 现实中的“投影”27 4.3 Go语言原生编程思维29 ●第二部分 项目结构、代码风格与标识符命名 第5条 使用得到公认且广泛使用的项目结构32 5.1 Go项目的项目结构32 5.2 Go语言典型项目结构35 第6条 提交前使用gofmt格式化源码40 6.1 gofmt:Go语言在解决规模化问题上的实践40 6.2 使用gofmt41 6.3 使用goimports43 6.4 将gofmt/goimports与IDE或编辑器工具集成44 第7条 使用Go命名惯例对标识符进行命名47 7.1 简单且一致48 7.2 利用上下文环境,让短的名字携带足够多的信息53 ●第三部分 声明、类型、语句与 控制结构 第8条 使用一致的变量声明形式56 8.1 包级变量的声明形式56 8.2 局部变量的声明形式59 第9条 使用无类型常量简化代码63 9.1 Go常量溯源63 9.2 有类型常量带来的烦恼64 9.3 无类型常量消除烦恼,简化代码65 第10条 使用iota实现枚举常量68 第11条 尽量定义零值可用的类型73 11.1 Go类型的零值73 11.2 零值可用75 第12条 使用复合字面值作为初值构造器78 12.1 结构体复合字面值79 12.2 数组/切片复合字面值80 12.3 map复合字面值81 第13条 了解切片实现原理并高效使用83 13.1 切片究竟是什么83 13.2 切片的高级特性:动态扩容87 13.3 尽量使用cap参数创建切片90 第14条 了解map实现原理并高效使用92 14.1 什么是map92 14.2 map的基本操作93 14.3 map的内部实现97 14.4 尽量使用cap参数创建map103 第15条 了解string实现原理并高效使用105 15.1 Go语言的字符串类型105 15.2 字符串的内部表示110 15.3 字符串的高效构造112 15.4 字符串相关的高效转换115 第16条 理解Go语言的包导入120 16.1 Go程序构建过程121 16.2 究竟是路径名还是包名127 16.3 包名冲突问题130 第17条 理解Go语言表达式的求值顺序132 17.1 包级别变量声明语句中的表达式求值顺序133 17.2 普通求值顺序136 17.3 赋值语句的求值139 17.4 switch/select语句中的表达式求值140 第18条 理解Go语言代码块与作用域143 18.1 Go代码块与作用域简介143 18.2 if条件控制语句的代码块145 18.3 其他控制语句的代码块规则简介148 第19条 了解Go语言控制语句惯用法及使用注意事项154 19.1 使用if控制语句时应遵循“快乐路径”原则154 19.2 for range的避“坑”指南156 19.3 break跳到哪里去了165 19.4 尽量用case表达式列表替代fallthrough167 ●第四部分 函数与方法 第20条 在init函数中检查包级变量的初始状态170 20.1 认识init函数170 20.2 程序初始化顺序171 20.3 使用init函数检查包级变量的初始状态174 第21条 让自己习惯于函数是“一等公民”179 21.1 什么是“一等公民”179 21.2 函数作为“一等公民”的特殊运用183 第22条 使用defer让函数更简洁、更健壮192 22.1 defer的运作机制193 22.2 defer的常见用法194 22.3 关于defer的几个关键问题199 第23条 理解方法的本质以选择 正确的receiver类型206 23.1 方法的本质207 23.2 选择正确的receiver类型208 23.3 基于对Go方法本质的理解巧解难题210 第24条 方法集合决定接口实现214 24.1 方法集合215 24.2 类型嵌入与方法集合216 24.3 defined类型的方法集合226 24.4 类型别名的方法集合227 第25条 了解变长参数函数的妙用230 25.1 什么是变长参数函数230 25.2 模拟函数重载233 25.3 模拟实现函数的可选参数与默认参数236 25.4 实现功能选项模式238 ●第五部分 接口 第26条 了解接口类型变量的内部表示246 26.1 nil error值 != nil247 26.2 接口类型变量的内部表示248 26.3 输出接口类型变量内部表示的详细信息254 26.4 接口类型的装箱原理258 第27条 尽量定义小接口263 27.1 Go推荐定义小接口263 27.2 小接口的优势265 27.3 定义小接口可以遵循的一些点267 第28条 尽量避免使用空接口作为函数参数类型270 第29条 使用接口作为程序水平组合的连接点274 29.1 一切皆组合274 29.2 垂直组合回顾275 29.3 以接口为连接点的水平组合276 第30条 使用接口提高代码的可测试性281 30.1 实现一个附加免责声明的电子邮件发送函数282 30.2 使用接口来降低耦合283 ●第六部分 并发编程 第31条 优先考虑并发设计288 31.1 并发与并行288 31.2 Go并发设计实例290 第32条 了解goroutine的调度原理299 32.1 goroutine调度器299 32.2 goroutine调度模型与演进过程300 32.3 对goroutine调度器原理的进一步理解302 32.4 调度器状态的查看方法305 32.5 goroutine调度实例简要分析307 第33条 掌握Go并发模型和常见并发模式315 33.1 Go并发模型315 33.2 Go常见的并发模式317 第34条 了解channel的妙用340 34.1 无缓冲channel341 34.2 带缓冲channel347 34.3 nil channel的妙用354 34.4 与select结合使用的一些惯用法357 第35条 了解sync包的正确用法359 35.1 sync包还是channel359 35.2 使用sync包的注意事项360 35.3 互斥锁还是读写锁362 35.4 条件变量365 35.5 使用sync.Once实现单例模式 368 35.6 使用sync.Pool减轻垃圾回收压力370 第36条 使用atomic包实现伸缩性更好的并发读取374 36.1 atomic包与原子操作374 36.2 对共享整型变量的无锁读写375 36.3 对共享自定义类型变量的无锁读写377 ●第七部分 错误处理 第37条 了解错误处理的4种策略382 37.1 构造错误值383 37.2 透明错误处理策略385 37.3 “哨兵”错误处理策略385 37.4 错误值类型检视策略388 37.5 错误行为特征检视策略390 第38条 尽量优化反复出现的if err != nil392 38.1 两种观点393 38.2 尽量优化395 38.3 优化思路395 第39条 不要使用panic进行正常的错误处理405 39.1 Go的panic不是Java的checked exception405 39.2 panic的典型应用408 39.3 理解panic的输出信息412