Files
CategoryResourceRepost/极客时间专栏/左耳听风/高效学习/99 | 高效学习:如何学习和阅读代码.md
louzefeng d3828a7aee mod
2024-07-11 05:50:32 +00:00

137 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<audio id="audio" title="99 | 高效学习:如何学习和阅读代码" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/12/68/12cc434cd3c3077814539be765d22568.mp3"></audio>
# 读文档还是读代码
杰夫·阿特伍德Jeff Atwood说过这么一句话“[Code Tells You How, Comments Tell You Why](https://blog.codinghorror.com/code-tells-you-how-comments-tell-you-why/)”。我把其扩展一下:
- **代 码 =&gt; What, How &amp; Details**
- **文档/书 =&gt; What, How &amp; Why**
可见,**代码并不会告诉你 Why**看代码只能靠猜测或推导来估计Why是揣测不准确所以会有很多误解。**而且我们每个人都知道Why是能让人一通百通的东西也是能让人醍醐灌顶的东西**。
但是,**代码会告诉你细节**,这是书和文档不能给你的。**细节是魔鬼,细节决定成败**。这样的话我们不但听过很多我们做技术的也应该体会过很多。当然我们也要承认这些代码细节给人带来的快感毕竟不如知道Why后的快感大至少对我是这样的
**书和文档是人对人说的话,代码是人对机器说的话**(注:代码中有一部份逻辑是控制流程的逻辑,不是业务逻辑)。所以:
<li>
**如果你想知道人为什么要这么搞,那么应该去看书**像Effective C++、Code Complete、Design Pattern、Thinking in Java等**看文档**。
</li>
<li>
**如果你要知道让机器干了什么?那你应该看代码!**就像Linus去看zlib的代码来找性能问题。
</li>
因此,我认为都比较重要,关键看你的目的是什么了。
<li>
**如果你想了解一种思想,一种方法,一种原理,一种思路,一种经验,恐怕,读书和读文档会更有效率一些**因为其中会有作者的思路描述。像Effective C++之类的书里面有很多对不同用法和设计的推敲TCP/IP详解里面也会有对TCP算法好坏的比较……这些思维方式能让你对技术的把握力更强而光看代码很难达到这种级别。现在你知道什么样的书是好书了吧 ;-)
</li>
<li>
**如果你想了解的就是具体细节,比如某协程的实现,某个模块的性能,某个算法的实现,那么你还是要去读代码的**因为代码中会有更具体的处理细节尤其是对于一些edge case或是代码技巧方面的内容
</li>
另外,看看下面的几个现象,你可以自己比较一下。
<li>
很多时候,我们去读代码,那是因为没有文档,或是文档写得太差。
</li>
<li>
很多时候,**在Google、Stack Overflow、GitHub过后你会发现你掌握的知识就是一块一块的碎片既不系统也不结构化更别说融会贯通了。你会觉得自己需要好好地读一本书系统地掌握知识。你的这种感觉一定很强烈吧**。
</li>
<li>
很多时候在读别人代码的时候你会因为基础知识或是原理不懂或是你在不知道为什么的情况下要么完全读不懂代码要么会误解代码。比如如果你没有C语言和TCP原理方面的基础知识就根本读不懂Linux下TCP的相关代码。我们因为误解代码用意而去修改代码造成的故障还少吗
</li>
<li>
很多时候看到一个算法或是一个设计时比如Paxos你是不是会想去看一下这个算法的实现代码是什么样的思考一下如何才能实现得好但是如果你没看过Paxos的算法思想我不认为你光看代码实现就能收获Paxos的思想。
</li>
<li>
很多时候,**当你写代码的时候,你能感觉得到自己写的代码有点别扭,怎么写都别扭,这个时候,你也会有想去看别人的代码是怎么实现的冲动**。
</li>
类似的情况还有很多,但从代码中收获大,还是从书中收获大,在不同的场景、不同的目的下,会有不同的答案。这里,谈一谈人的学习过程吧。从学习的过程中,我们来分析一下看代码和看书这两个活动。人对新事物的学习过程基本都是从“感性认识”到“理性认识”的。
<li>
**如果你是个新手,那应该多读代码,多动手写代码**因为你需要的是“感性认识”这个时候“理性认识”你体会不到。一是因为你没有切身的感受即便告诉你Why你也体会不到。另一方面这个阶段你要的不是做漂亮而是做出来。所以在**新手阶段你会喜欢GitHub这样的东西**。
</li>
<li>
**如果你是个老手,你有多年的“感性认识”了,那么你的成长需要更多的“理性认识”**。因为这个阶段一方面你会不满足于做出来你会想去做更牛更漂亮的东西另一方面你知道的越多你的问题也越多你迫切地需要知道Why这时你需要大量地找牛人交流读牛人的书是一种特殊的人与人的交流所以**这个阶段,你会喜欢读好的书和文章**。
</li>
然而,对于计算机行业这个技术创新能力超强、技术种类繁多的行业来说,我们每个人都既是新手,也是老手。
# 如何阅读源代码
很多人问过我如何读代码。因为我在外企里工作的时间较长所以我经常接手一些国外团队写的代码。我发现虽然老外写的代码比国人好一点儿有Code Review但依然有文档缺失、代码注释不清、代码风格混乱等一些问题这些都是阅读代码的障碍。这里我把我的一些阅读源代码的经验分享给你希望对你有用。
首先,在阅读代码之前,我建议你需要有下面的这些前提再去阅读代码,这样你读起代码来会很顺畅。
<li>
**基础知识**。相关的语言和基础技术的知识。
</li>
<li>
**软件功能**。你先要知道这个软件完成的是什么样的功能,有哪些特性,哪些配置项。你先要读一遍用户手册,然后让软件跑起来,自己先用一下感受一下。
</li>
<li>
**相关文档**。读一下相关的内部文档Readme也好Release Notes也好Design也好Wiki也好这些文档可以让你明白整个软件的方方面面。如果你的软件没有文档那么你只能指望这个软件的原作者还在而且他还乐于交流。
</li>
<li>
**代码的组织结构**。也就是代码目录中每个目录是什么样的功能每个文档是干什么的。如果你要读的程序是在某种标准的框架下组织的比如Java的Spring框架那么恭喜你这些代码不难读了。
</li>
接下来,你要了解这个软件的代码是由哪些部分构成的,我在这里给你一个列表,供你参考。
<li>
**接口抽象定义**。任何代码都会有很多接口或抽象定义,其描述了代码需要处理的数据结构或者业务实体,以及它们之间的关系,理清楚这些关系是非常重要的。
</li>
<li>
**模块粘合层**。我们的代码有很多都是用来粘合代码的比如中间件middleware、Promises模式、回调Callback、代理委托、依赖注入等。这些代码模块间的粘合技术是非常重要的因为它们会把本来平铺直述的代码给分裂开来让你不容易看明白它们的关系。
</li>
<li>
**业务流程**。这是代码运行的过程。一开始,我们不要进入细节,但需要在高层搞清楚整个业务的流程是什么样的,在这个流程中,数据是怎么被传递和处理的。一般来说,我们需要画程序流程图或者时序处理图。
</li>
<li>
**具体实现**。了解上述的三个方面的内容,相信你对整个代码的框架和逻辑已经有了总体认识。这个时候,你就可以深入细节,开始阅读具体实现的代码了。对于代码的具体实现,一般来说,你需要知道下面一些事实,这样有助于你在阅读代码时找到重点。
<ul>
<li>
**代码逻辑**。代码有两种逻辑一种是业务逻辑这种逻辑是真正的业务处理逻辑另一种是控制逻辑这种逻辑只是用控制程序流转的不是业务逻辑。比如flag之类的控制变量多线程处理的代码异步控制的代码远程通讯的代码对象序列化反序列化的代码等。这两种逻辑你要分开很多代码之所以混乱就是把这两种逻辑混在一起了详情参看《编程范式游记》
</li>
<li>
**出错处理**。根据二八原则20%的代码是正常的逻辑80%的代码是在处理各种错误,所以,你在读代码的时候,完全可以把处理错误的代码全部删除掉,这样就会留下比较干净和简单的正常逻辑的代码。排除干扰因素,可以更高效地读代码。
</li>
<li>
**数据处理**。只要你认真观察就会发现我们好多代码就是在那里倒腾数据。比如DAO、DTO比如JSON、XML这些代码冗长无聊不是主要逻辑可以不理。
</li>
<li>
**重要的算法**。一般来说我们的代码里会有很多重要的算法我说的并不一定是什么排序或是搜索算法可能会是一些其它的核心算法比如一些索引表的算法全局唯一ID的算法、信息推荐的算法、统计算法、通读算法如Gossip等。这些比较核心的算法可能会非常难读但它们往往是最有技术含量的部分。
</li>
<li>
**底层交互**。有一些代码是和底层系统的交互一般来说是和操作系统或是JVM的交互。因此读这些代码通常需要一定的底层技术知识不然很难读懂。
</li>
</ul>
</li>
<li>
**运行时调试**。很多时候代码只有运行起来了才能知道具体发生了什么事所以我们让代码运行进来然后用日志也好debug设置断点跟踪也好。实际看一下代码的运行过程是了解代码的一种很好的方式。
</li>
总结一下,阅读代码的方法如下:
- 一般采用自顶向下,从总体到细节的“剥洋葱皮”的读法。
- 画图是必要的,程序流程图,调用时序图,模块组织图……
- 代码逻辑归一下类,排除杂音,主要逻辑才会更清楚。
- debug跟踪一下代码是了解代码在执行中发生了什么的最好方式。
对了阅读代码你需要一个很好的IDE。我记得以前读C和C++代码时有一个叫source insight的工具就大大提高了我的代码阅读效率。说白了就是可以查看代码间相互的调用reference的工具这方面Visual Studio做得是非常好的。
# 小结
总结一下今天的内容。我先跟你探讨了“是读文档,还是读代码”,分析对比了从文档和代码中各自能收获到哪些东西,然后给出建议,如果想了解思想、方法和原理,读书和读文档会更有效率;如果想知道具体细节,还是应该读代码。随后分享了一些我阅读代码和源代码时候的方法和技巧。希望对你有启发。
下篇文章是《高效学习》系列的最后一篇,我将分享一下面对枯燥和量大的知识时,我们该怎样做。
下面是《高效学习》系列文章的目录。
- [端正学习态度](https://time.geekbang.org/column/article/14271)
- [源头、原理和知识地图](https://time.geekbang.org/column/article/14321)
- [深度,归纳和坚持实践](https://time.geekbang.org/column/article/14360)
- [如何学习和阅读代码](https://time.geekbang.org/column/article/14380)
- [面对枯燥和量大的知识](https://time.geekbang.org/column/article/14389)