https://mp.weixin.qq.com/s/8Wjeva-4c628pPSZPlcO3w
https://segmentfault.com/a/1190000013561054
http://www.10tiao.com/html/674/201803/2656597204/1.html
1、运行代码
这是读代码的第一步。虽然你还不能全部了解此项目的细节,但我们知道如何构建并运行它,也会了解到它使用了哪些库,依赖的框架等,这对我们提高指定的项目理解是个好方法。
2、找到高级逻辑
当开始阅读一个项目时,我们不可能跳进每个细节。相反,你应该关注高层结构,从入口点开始。大多数项目都有一个主方法,我们就从那里开始。
比如是一个Java Web应用程序,我们可以从研究不同的包开始,比如业务逻辑存放的位置,UI代码的位置,控制器的位置等。基本上,浏览整个项目需要找到主要的想法,然后想想自己想关注的位置或想先读的部分。
我们可能不会阅读整个代码库,而只是对其中的一部分感兴趣,可以从某个方法调用开始,让它带着你开始。
3、使用/了解工具
现在也有很多工具可以帮助开发者阅读和浏览代码,使源代码更加可视化。例如,IntelliJIdea有代码导航的功能,可以通过单词,部分关键字等进行搜索。
我们开发者应该擅用键盘快捷键,可以会让你的工作效率提升很多。
有一个软件叫做Sourcegraph,它对我们阅读代码很方便。它由两个斯坦福大学毕业生Quinn Slack和Beyang Liu开发。开发这款软件的原因是,以前他们曾花了多个小时的时间来搜索那些记录不完整的代码,于是决定构建一个工具来帮助更好地阅读代码。
4、了解语言/习惯
深入了解某种语言有助于提高开发者的阅读代码能力。每种开发语言都有自己的一套规约、样式和语法,这些知识可以帮助我们快速熟悉特定的语言代码。
比如在Java中,方法名称以小写字母开头,而在C#中 ,它们以大写字母开头。
5、阅读最佳实践/设计模式
6、代码审查
7、临时重构
http://www.10tiao.com/html/697/201802/2656330440/1.html
http://www.spinellis.gr/codereading/
http://book.douban.com/review/5634843/
1.1.1 将代码作为文献
1. 要养成一个习惯,经常花时间阅读别人编写的高品质代码。 (读好书、多读书) 就像阅读高品质的散文能够丰富词汇、激发想象力、扩展思维一样,分析设计良好的软件系统的内部结构可以学到新的架构模式、数据结构、编码方法、算法、风格和文档规范、应用程序编程接口(API),甚至新的计算机语言。阅读高品质的代码还可以提高您编写代码的水准。
1.1.3 维护
7. 在寻找BUG时,请从问题的表现形式到问题的根源来分析代码。不要沿着不相关的路径(误入岐途) (先搞清楚问题,然后找与问题相关部分代码阅读,记住带有目的性的阅读)
8. 寻找bug,这种情况下,关键的思想是使用工具,我们要充分利用调试器,编译器给出的警告或输出的符号代码,系统调用跟踪器,数据库结构化查询语言(SQL)的日志机制、包转储工具和Windows的消息侦查程序,定出BUG的位置。(要充分利用线索。 侦探破案、医生诊断 都是利用线索或现象的)
150.阅读代码时, 应该尽可能地利用任何能够得到的文档.
http://blog.csdn.net/gaohuaid/article/details/27098071
作者描述的代码阅读的主要方法就是:定位,细读,操作(修改,增加,重构),检查。
怎样鉴别低品质的代码:
1.编码风格不一致;
2.结构上毫无道理的复杂或可读性差;
3.明显的逻辑错误或疏忽;
4.过度使用不可移植的结构;
5.缺乏维护。
http://www.cnblogs.com/zhaorui/archive/2012/12/05/20121205_ReadCode_Note_01.html
http://book.douban.com/review/5634843/
http://pythonpracticeprojects.com/how-to-read-source-code.html
1. Find and establish an initial base for your mind to latch onto. Usually the main entry point. 2. Start from your base and explore the major features. 3. Take notes on what you've seen.
By exposing yourself to a variety of styles, you'll eventually develop a unique perspective and voice.
Take Notes
I tend to take notes right in the source code. When writing I use a special comment character (e.g #=> instead of the typical #) so that I can distinguish between my own notes and the original author's comments.
Make a note on all the clever tricks, confusing flows, beautiful usgae of programming constructs, and anything else you want to remember. If you're stuck, you can also make a note about coming back to that particular section.
By writing down your thoughts, you're really making that piece of source code your own. Over time, the constructs you pick up will start leaking into your own works.
http://www.jianshu.com/p/3e6d4c520719
其次,高效地使用快捷键,这是一个良好的代码阅读习惯,它极大地提高了代码阅读的效率和质量。例如,查看类层次关系,函数调用链,方法引用点等等。
但在阅读代码之前,有几件事情是必须做的。其一,手动地构建一次工程,并运行测试用例;其二,亲自动手写几个
先将工程跑起来,目的不是为了
如果条件允许,可以尝试使用
首要的任务,就是找到系统的边界,并能够以「抽象的思维」思考外部系统的行为特征。其次,寻找系统潜在的,并能表达系统的重要概念,及其它们之间的关联关系。
阅读代码的一个常见的反模式就是「给代码做批注」。这是一个高耗低效,投入产出比极低的实践。越是优雅的系统,注释越少;越是复杂的系统,再多的注释也是于事无补。
我有一个代码阅读的习惯,为代码阅读建立一个单独的
还有另外一个常见的反模式就是「追踪函数调用栈」。这是一个极度枯燥的过程,常常导致思维的僵化;因为你永远活在作者的阴影下,完全没有自我。
我个人阅读代码的时候,函数调用栈深度绝不超过
也就是说,我更习惯于「广度遍历」,而不习惯于「深度遍历」的阅读方式。这样,我才能找到系统隐晦存在的「分层概念」,并理顺系统的结构。
当我发现一个好的设计时,我会尝试使用类图,状态机,时序图等方式来表达设计;如果发现潜在的不足,将自己的想法补充进去,将更加完美。
例如,当我阅读
对于一个巨函数,我常常会提取出一个抽象的代码层次,以便发现它潜在的本质逻辑。例如,这是一个
我更倾向于使用「形式化」的方式来描述问题。它拥有数学的美感,简洁的表达方式,及其高度抽象的思维,对挖掘问题本质极其关键。
例如,对于
以
如果存在某个算法和实现都相当复杂时,也可以通过实例化探究算法的工作原理,这对于理解问题本身大有益处。
以
也可以通过各种渠道,收集他人的经验,并结合自己的思考,推敲出自己的理解,如此才能将知识放入自己的囊中。
http://www.finalshares.com/read-7029
先选择合适的源码版本
因为不同的版本间的特性/流程方面存在较大的差异,阅读源码时选择合适的版本还是至关重要的。因此,需要先审视自己的需求:
“我阅读源码,是单纯的为了学习?还是希望在业务系统中更好的用好它?”
如果是前者,那完全可以选择最新发布或待发布的稳定版本。
如果是后者,则需要选择自己业务系统中正在使用的版本。
借助书籍或官方资料快速了解技术架构和关键特性
如果有介绍原理的书籍,可以先快速浏览一遍,粗略了解整体架构、关键特性。
这些信息也可以从官方资料中一探究竟,尤其是架构介绍相关的章节。
从快速试用开始加强自己对该项目的感性认识
先参考官方资料中的Quick Start章节,先学习如何使用,加强自己对于整体项目的感性认识。
这个过程,基本能摸清楚利用该项目"能做什么",以及"如何做"。当然,这里仅仅涉及了最基础的功能。
简单了解源码模块结构,而后从最基础的流程入手
快速了解源码的模块组成结构,以及每一个模块的主要作用。
这样有助于从源码结构上把握整体项目的轮廓,而后选择最基础的流程入手。
对于HBase而言,最基础的流程无非是如何建表以及如何写数据的流程。
学习一个特性要从了解配置和如何使用着手,同时建议阅读相关特性的设计文档或网上已有的源码解析文章
在学习一个特性时,也应该先从如何使用这个特性开始,接口如何被调用,关键配置有哪些,都是了解基础功能的基本起点。
接下来,可以先自己思考一下,这个特性如果由自己来设计,那整体思路应该是怎样的。
部分关键的特性/流程,在社区的问题单中,通常会有简洁的设计文档,这些文档能帮你理清方案的整体框架和思路。如果没有设计文档,那问题单中的Comments也是值得参考的。
当然,网上如果已经存在一些源码解析文章,也可以先参考一下,但好的文章往往是可遇不可求的。
如果在阅读源码之前,能够大致了解方案的思路,对自己会有很大的帮助,"瞎子摸象"式的阅读非常费时费力。
有一点需要强调一下:书籍或别人的文章中所描述的流程,在新版本中有可能已经发生了变化,因此,阅读时一定要带着辨证的思维。
摸清主线,避免过早陷入一些旁枝末节
刚开始阅读源码时,会遇到很多"好奇点":
- 这个算法居然实现的如此神奇?
- 这个数据结构怎么没有见过?
- 这个参数是干嘛的?
我自己也时常经不起这些"诱惑",陷于对这些细节的考究中,常常"离题"半天以后,才被拉回到主线中。
在阅读源码的时候,能遇到一些感兴趣的细节是好事,但建议先将这些细节点记录下来,等过完整体流程以后再回头看这些细节,避免过早陷入。
阅读源码过程中,通常需要动手做一些测试,此时,可以借助jstack工具(针对Java项目),它能为你提供如下有价值的信息:
- 线程模型
- 调用栈
调用栈信息可以帮你理清整体调用流程(另外,在定位问题时,jstack打印出的信息也时常可以发挥重要作用)。
阅读源码过程中,同步绘制时序图,固化对流程的理解
好不容易摸清的主线,建议及时用时序图的方式固化下来,这样可以帮助自己快速回顾整个流程。
当然,除了时序图,还建议附带简单的文字性总结。
阅读源码过程中,不断发现或提出疑问,并且记下来
当理清了主线流程以后,要继续深入探索这些细节疑问点,这些点决定了你对整个特性/流程的理解深度。
掌握一个特性/流程的基本前提,就是需要自己解答自己提出的所有疑问。
对于一些"莫名其妙"或"匪夷所思"的设计,请一定要对照参考社区问题单中的描述信息、设计文档或Comments信息。
阅读源码过程中,遇到晦涩难懂的细节,如何应对?
此时,建议开启Debug模式,详细跟踪每一步的调用流程,Debug可以分两种形式:
- 远程Debug
- 本地Debug
对于HBase而言,相比于远程Debug,本地Debug似乎更难以理解了,因为我们所熟知的HBase部署形态就是分布式的,要对运行时的HBase集群进行Debug,自然采用远程Debug模式了。
其实,Debug也可以针对HBase提供的测试用例,大部分用例都是基于一个本地模拟的Mini Cluster运行的,这个Mini Cluster运行在一个进程中,使用线程模拟HBase的关键进程。
这个过程中,也可以动手小改一下源码,验证自己的想法,或者观察因为改动所带来的行为变化。
重视阅读测试用例源码
很多人并不习惯于阅读HBase的测试用例源码,其实,阅读测试用例的源码,可以帮你理解一些正确的行为应该是怎样的。
因为每一个被定义的正确行为,都以具体的测试用例固化下来了。
重视实际遇到的每一个Bug,每一个Bug都可以讲一个完整的故事
阅读源码过程中,自己提出的疑问,往往还不是最深刻的。最深刻的点,往往存在于所遇到的每一个Bug中。
对于Bug,很多人的态度往往是,能规避则规避之,集群只要能恢复正常,就不再有任何兴致去探究根因。
Bug往往是一些未考虑充足的边界场景,如果想探究Bug的根因,必然需要先摸清与之相关的所有流程,而后结合问题现象进行相关推理。一个Bug的前因后果,通常可以讲一个完整的故事。只有经过一个个Bug的历练,才能逐步成长为内核专家。
能力进阶:开始关注社区动态,或尝试为社区贡献Patch
关注社区动态,可以及时获知一些重要的Bugs或社区正在开发的大的Features。关注的方式包括但不限于:
- 订阅社区的Mail List
- 关注社区的问题单
如果感觉自己已经很好的掌握了源码,而且发现了部分设计不合理,或者是部分能力不完善(结合实际的业务需求),也可以主动为社区贡献Patch,对于大部分开源项目而言,都是非常鼓励大家贡献Patch的。
我认为是阅读源码的最核心驱动力。我见到绝大多数程序员,对学习的态度,基本上就是这几个层次(很偏激哦):
- 1、只关注项目本身,不懂就baidu一下。
- 2、除了做好项目,还会阅读和项目有关的技术书籍,看wikipedia。
- 3、除了阅读和项目相关的书外,还会阅读IT行业的书,比如学Java时,还会去了解函数语言,如LISP。
- 4、找一些开源项目看看,大量试用第三方框架,还会写写demo。
- 5、阅读基础框架、J2EE规范、Debug服务器内核。
大多数程序都是第1种,到第5种不光需要浓厚的兴趣,还需要勇气
https://about.sourcegraph.com/http://www.10tiao.com/html/674/201803/2656597204/1.html
1、运行代码
这是读代码的第一步。虽然你还不能全部了解此项目的细节,但我们知道如何构建并运行它,也会了解到它使用了哪些库,依赖的框架等,这对我们提高指定的项目理解是个好方法。
2、找到高级逻辑
当开始阅读一个项目时,我们不可能跳进每个细节。相反,你应该关注高层结构,从入口点开始。大多数项目都有一个主方法,我们就从那里开始。
比如是一个Java Web应用程序,我们可以从研究不同的包开始,比如业务逻辑存放的位置,UI代码的位置,控制器的位置等。基本上,浏览整个项目需要找到主要的想法,然后想想自己想关注的位置或想先读的部分。
我们可能不会阅读整个代码库,而只是对其中的一部分感兴趣,可以从某个方法调用开始,让它带着你开始。
3、使用/了解工具
现在也有很多工具可以帮助开发者阅读和浏览代码,使源代码更加可视化。例如,IntelliJIdea有代码导航的功能,可以通过单词,部分关键字等进行搜索。
我们开发者应该擅用键盘快捷键,可以会让你的工作效率提升很多。
有一个软件叫做Sourcegraph,它对我们阅读代码很方便。它由两个斯坦福大学毕业生Quinn Slack和Beyang Liu开发。开发这款软件的原因是,以前他们曾花了多个小时的时间来搜索那些记录不完整的代码,于是决定构建一个工具来帮助更好地阅读代码。
4、了解语言/习惯
深入了解某种语言有助于提高开发者的阅读代码能力。每种开发语言都有自己的一套规约、样式和语法,这些知识可以帮助我们快速熟悉特定的语言代码。
比如在Java中,方法名称以小写字母开头,而在C#中 ,它们以大写字母开头。
5、阅读最佳实践/设计模式
6、代码审查
7、临时重构
http://www.10tiao.com/html/697/201802/2656330440/1.html
请使用 git blame 找出提交者,找到他或她去询问。
使用 git log 查看整个代码仓库的提交历史记录。此命令将打印提交消息,因此,如果要执行类似搜索提交消息中引用 someFunction 的提交,请勿忘记使用 grep 命令:git log | grep someFunction -C 3(-C 3将显示匹配到的上下文三行内容)。
git log 还可以显示具有 -p 标志的单个文件的历史记录:git log -p index.js。 注意最近一直在修改代码的人,这样你就能知道在出现问题时找谁咨询了。
http://www.spinellis.gr/codereading/
http://book.douban.com/review/5634843/
1.1.1 将代码作为文献
1. 要养成一个习惯,经常花时间阅读别人编写的高品质代码。 (读好书、多读书) 就像阅读高品质的散文能够丰富词汇、激发想象力、扩展思维一样,分析设计良好的软件系统的内部结构可以学到新的架构模式、数据结构、编码方法、算法、风格和文档规范、应用程序编程接口(API),甚至新的计算机语言。阅读高品质的代码还可以提高您编写代码的水准。
1.1.3 维护
7. 在寻找BUG时,请从问题的表现形式到问题的根源来分析代码。不要沿着不相关的路径(误入岐途) (先搞清楚问题,然后找与问题相关部分代码阅读,记住带有目的性的阅读)
8. 寻找bug,这种情况下,关键的思想是使用工具,我们要充分利用调试器,编译器给出的警告或输出的符号代码,系统调用跟踪器,数据库结构化查询语言(SQL)的日志机制、包转储工具和Windows的消息侦查程序,定出BUG的位置。(要充分利用线索。 侦探破案、医生诊断 都是利用线索或现象的)
150.阅读代码时, 应该尽可能地利用任何能够得到的文档.
http://blog.csdn.net/gaohuaid/article/details/27098071
作者描述的代码阅读的主要方法就是:定位,细读,操作(修改,增加,重构),检查。
怎样鉴别低品质的代码:
1.编码风格不一致;
2.结构上毫无道理的复杂或可读性差;
3.明显的逻辑错误或疏忽;
4.过度使用不可移植的结构;
5.缺乏维护。
http://www.cnblogs.com/zhaorui/archive/2012/12/05/20121205_ReadCode_Note_01.html
要有选择的阅读代码。
从小型的程序开始阅读;
编译研究的程序,并运行它们;
主动修改代码来检验对代码的理解是否准确。改进它。
http://www.cnblogs.com/zhaorui/archive/2012/12/05/20121205_ReadCode_Note_01.html从小型的程序开始阅读;
编译研究的程序,并运行它们;
主动修改代码来检验对代码的理解是否准确。改进它。
- Make it a habit to spend time reading high-quality code that others have written.
- Read code selectively and with a goal in your mind. Are you trying to learn new patterns, a coding style, a way to satisfy some requirements?
- Notice and appreciate the code's particular nonfunctional requirements that might give rise to a specific implementation style.
- When working on existing code, coordinate your efforts with the authors or maintainers to avoid duplication of work or bad feelings.
- Consider the benefits you receive from open-source software to be a loan; look for ways to repay it by contributing back to the open-source community.
- In many cases if you want to know "how did they do that?" there's no better way than reading the code.
- when looking for a bug, examine the code from the problem manifestation to the problem source. Avoid following unrelated paths.
- Use the debugger, the compiler's warning or symbolic code output, a system call tracer, your database's SQL logging facility, packet dump tools, and windows message spy programs to locate a bug's location.
- You can successfully modify large well-structured system with only a minimal understanding of their complete functionality.
- When adding new functionality to a system, your first task is to find the implementation of a similar feature to use as a template for the one you will be implementing.
- To go from a feature's functional specification to the code implementation, follow the string messages or search the code using keywords.
- When porting code or modifying interfaces, you can save code-reading effort by directing your attention to the problem areas identified by the compiler.
- When refactoring, you start with a working system and want to ensure that you will end up with a working one. A suite of pertinent test cases will help you satisfy this obligation.
- When reading code to search for refactoring opportunities, you can maximize your return on investment by starting from the system's architecture and moving downward, looking at increasing levels of detail.
- Code reusability is a tempting but elusive concept; limit your expectations and you will not be disappointed.
- If the code you want to reuse is intractable and difficult to understand and isolate, look at larger granularity packages or different code.
- while reviewing a software system, keep in mind that it consists of more elements than executable statements. Examine the file and directory structure the build and configuration process, the user interface, and the system's documentation.
- Use software reviews as a chance to learn, teach, lend a hand, and receive assistance.
- Sometimes executing a program can be a more expedient way to understand an aspect of its functionality than reading its source code.
- When examining a nontrivial program, it is useful to first identify its major constituent parts.
- Learn local naming conventions and use them to guess what variables and functions do.
- Code reading involves many alternative strategies: bottom-up and top-down examination, the use of heuristics, and review of comments and external documentation should all be tried as the problem dictates.
- You can often understand the meaning of an expression by applying it on sample data.
- Reorganize code you control to make it readable.
- Read expressions using the conditional operator ?: like if code.
- There is no need to sacrifice code readability for efficiency.
- while it is true that efficient algorithms and certain optimizations can make the code more complicated and therefore more difficult to follow, this does not mean that making the code compact and unreadable will make it more efficient.
- When reading code under your control, make it a habit to add comments as needed.
- You can improve the readability of poorly written code with better indentation and appropriate variable names.
http://book.douban.com/review/5634843/
http://pythonpracticeprojects.com/how-to-read-source-code.html
1. Find and establish an initial base for your mind to latch onto. Usually the main entry point. 2. Start from your base and explore the major features. 3. Take notes on what you've seen.
By exposing yourself to a variety of styles, you'll eventually develop a unique perspective and voice.
Take Notes
I tend to take notes right in the source code. When writing I use a special comment character (e.g #=> instead of the typical #) so that I can distinguish between my own notes and the original author's comments.
Make a note on all the clever tricks, confusing flows, beautiful usgae of programming constructs, and anything else you want to remember. If you're stuck, you can also make a note about coming back to that particular section.
By writing down your thoughts, you're really making that piece of source code your own. Over time, the constructs you pick up will start leaking into your own works.
http://www.jianshu.com/p/3e6d4c520719
其次,高效地使用快捷键,这是一个良好的代码阅读习惯,它极大地提高了代码阅读的效率和质量。例如,查看类层次关系,函数调用链,方法引用点等等。
拔掉鼠标,减低对鼠标的依赖。当发现没有鼠标而导致工作无法进行下去时,尝试寻找对应的快捷键。通过日常的点滴积累,工作效率必然能够得到成倍的提高。
力行而后知之真
阅读代码一种常见的反模式就是「通过Debug
的方式来阅读代码」。作者不推荐这种代码阅读的方式,其一,因为运行时线程间的切换很容易导致方向的迷失;其二,了解代码调用栈对于理解系统行为并非见得有效,因为其包含太多实现细节,不易发现问题的本质。但在阅读代码之前,有几件事情是必须做的。其一,手动地构建一次工程,并运行测试用例;其二,亲自动手写几个
Demo
感受一下。先将工程跑起来,目的不是为了
Debug
代码,而是在于了解工程构建的方式,及其认识系统的基本结构,并体会系统的使用方式。如果条件允许,可以尝试使用
ATDD
的方式,发现和挖掘系统的行为。通过这个过程,将自己当成一个客户,思考系统的行为,这是理解系统最重要的基石。发现领域模型
发现「领域模型」是阅读代码最重要的一个目标,因为领域模型是系统的灵魂所在。通过代码阅读,找到系统本质的模型,并通过自己的模式表达出来,你才能真正地Hold
住了系统,否则一切都是空谈。首要的任务,就是找到系统的边界,并能够以「抽象的思维」思考外部系统的行为特征。其次,寻找系统潜在的,并能表达系统的重要概念,及其它们之间的关联关系。
细节是魔鬼
纠结于细节,将导致代码阅读代码的效率和质量大大折扣。例如,日志打印,解决Bug
的补丁实现,某版本分支的兼容方案,某变态用户需求的锤子代码等等。阅读代码的一个常见的反模式就是「给代码做批注」。这是一个高耗低效,投入产出比极低的实践。越是优雅的系统,注释越少;越是复杂的系统,再多的注释也是于事无补。
我有一个代码阅读的习惯,为代码阅读建立一个单独的
code-reading
分支,一边阅读代码,一边删除这些无关的代码。$ git checkout -b code-reading
删除这些噪声后,你会发现系统根本没有想象之中那么复杂。事实上,系统的复杂性,往往都是之前不成熟的设计和实现导致的额外复杂度。适可而止
阅读代码的一个常见的反模式就是「一根筋走到底,不到黄河绝不死心」。程序员都拥有一颗好奇心,总是对不清楚的事情感兴趣。例如,消息是怎么发送出去的?任务调度工作原理是什么?数据存储怎么做到的等等;虽然这种勇气值得赞扬,但在代码阅读时绝对不值得鼓励。还有另外一个常见的反模式就是「追踪函数调用栈」。这是一个极度枯燥的过程,常常导致思维的僵化;因为你永远活在作者的阴影下,完全没有自我。
我个人阅读代码的时候,函数调用栈深度绝不超过
3
,然后使用抽象的思维方式思考底层的调用。因为我发现,随着年龄的增长,曾今值得骄傲的记忆力,现在逐渐地变成自己的短板。当我尝试追踪过深的调用栈之后,之前的阅读信息完全地消失记忆了。也就是说,我更习惯于「广度遍历」,而不习惯于「深度遍历」的阅读方式。这样,我才能找到系统隐晦存在的「分层概念」,并理顺系统的结构。
发现她的美
三人行,必有我师焉。在代码阅读代码时,当发现好的设计,包括实现模式,习惯用法等,千万不要错过;否则过上一段时间,这次代码阅读对你来说就没有什么价值了。当我发现一个好的设计时,我会尝试使用类图,状态机,时序图等方式来表达设计;如果发现潜在的不足,将自己的想法补充进去,将更加完美。
例如,当我阅读
Hamcrest
时,尝试画画类图,并体会它们之间关系,感受一下设计的美感,也是受益颇多的。尝试重构
因为这是一次代码阅读的过程,不会因为重构带来潜在风险的问题。在一些复杂的逻辑,通过重构的等价变换可以将其变得更加明晰,直观。对于一个巨函数,我常常会提取出一个抽象的代码层次,以便发现它潜在的本质逻辑。例如,这是一个
ArrayBuffer
的实现,当需要在尾部添加一个元素时,既有的设计是这样子的。def +=(elem: A): this.type = {
if (size + 1 > array.length) {
var newSize: Long = array.length
while (n > newSize)
newSize *= 2
newSize = math.min(newSize, Int.MaxValue).toInt
val newArray = new Array[AnyRef](newSize)
System.arraycopy(array, 0, newArray, 0, size)
array = newArray
}
array(size) = elem.asInstanceOf[AnyRef]
size += 1
this
}
这段代码给阅读造成了极大的障碍,我会通过快速的函数提取,发现逻辑的主干。def +=(elem: A): this.type = {
if (atCapacity)
grow()
addElement(elem)
}
至于atCapacity, grow, addElement
是怎么实现的,压根不用关心,因为我已经达到阅读代码的效果了。形式化
当阅读代码时,有部分人习惯画程序的「流程图」。相反,我几乎从来不会画「流程图」,因为流程图反映了太多的实现细节,而不能深刻地反映算法的本质。我更倾向于使用「形式化」的方式来描述问题。它拥有数学的美感,简洁的表达方式,及其高度抽象的思维,对挖掘问题本质极其关键。
例如,对于
FizzBuzzWhizz
的问题,相对于冗长的文字描述,流程图等方式,形式化的方式将更加简单,并富有表达力。以
3, 5, 7
为输入,形式化后描述后,可清晰地挖掘出问题的本质所在。r1: times(3) => Fizz ||
times(5) => Buzz ||
times(7) => Whizz
r2: times(3) && times(5) && times(7) => FizzBuzzWhizz ||
times(3) && times(5) => FizzBuzz ||
times(3) && times(7) => FizzWhizz ||
times(5) && times(7) => BuzzWhizz
r3: contains(3) => Fizz
rd: others => string of others
spec: r3 || r2 || r1 || rd
实例化
实例化是认识问题的一种重要方法,当逻辑非常复杂时,一个简单例子往往使自己豁然开朗。在理想的情况下,实例化可以做成自动化的测试用例,并以此描述系统的行为。如果存在某个算法和实现都相当复杂时,也可以通过实例化探究算法的工作原理,这对于理解问题本身大有益处。
以
Spark
中划分DAG
算法为例。假设G
为FinalRDD
,从后往前按照RDD
的依赖关系,依次识别出各个Stage
的起始边界。-
Stage 3
的划分:
-
G
与B
之间是Narrow Dependency
,规约为同一Stage(3)
; -
B
与A
之间是Wide Dependency
,A
为新的FinalRDD
,递归调用此过程; -
G
与F
之间是Wide Dependency
,F
为新的FinalRDD
,递归调用此过程;
-
-
Stage 1
的划分
-
A
没有父亲RDD
,Stage(1)
划分结束。特殊地Stage(1)
仅包含RDD A
;
-
-
Stage 2
的划分:
- 因
RDD
之间的关系都为Narrow Dependency
,规约为同一个Stage(2)
; - 直至
RDD C, E
,因没有父亲RDD
,Stage(2)
划分结束;
- 因
Stage
的依赖关系,依次提交Stage(TaskSet)
至TaskScheduler
进行调度执行。独乐乐不如众乐乐
与他人分享你的经验,也许可以找到更多的启发;尤其对于熟知该领域的人沟通,如果是Owner
就更好了,更能得到意外的惊喜和收获。也可以通过各种渠道,收集他人的经验,并结合自己的思考,推敲出自己的理解,如此才能将知识放入自己的囊中。
http://www.finalshares.com/read-7029
阅读源代码有许多益处。你会发现新的架构(construct)和库,与其他的代码维护者产生共鸣,但最重要的是学会如何组织代码,避免因内部极其复杂而变得不可维护。
但是也有一个不好的地方,那就是阅读源代码太困难了。每当我看到一个新的代码库(code base)时,这种让人眩晕的感觉就充斥了我的大脑。我的内心告诉我压根不想趟眼前这趟浑水。
这是(希望是)正常的反应。当我们的大脑接触过多的新东西,就会产生排斥。造物主赋予我们的这台强大的模式匹配机器根本找不到规律。所有的抽象(abstraction)都是之前没见过的,类的名称也毫无印象。程序又到底是从什么地方开始执行的?
对此,我能给出的一般性建议如下:1. 寻找并建立自己能够理解的初步基础,通常就是主要的入口点(main entry point)。2. 从这个基础开始,逐步探索主要功能。3. 记录下自己的见闻。
从头开始
窍门就是给自己一个起点。我是这样做的。我通过
-h
选项运行程序,并调用 help 命令。之后我复制其中一条 help 文档字符串,以此为检索词搜索一遍代码库,找到这个帮助文档所在地方。通常情况下,调用 help 命令之后你会发现离程序的主入口点很近了。
辨明轮廓
找到主入口点之后,我会运行几个文档中提供的示例。然后,我会试着追踪主要的代码块,大致了解下每个部分是如何连接起来的。
我会问自己,是否存在一个管理程序,负责调用一堆帮助函数和类(helper functions and classes)?是不是有一些类是平级关系,相互之间轮流交换控制权?是不是有一个程序逐步执行的主任务队列?
了解全局有助于你理清小细节。如果你没有理解程序的主流程就闷头读下去,那你很可能会被细枝末节搞得焦头烂额。
勤做笔记
我习惯直接在代码中做笔记。做笔记的时候,我会使用特殊的注释符(例如,使用
#=>
,而非常用的#
),这样可以将我自己的笔记与原作者注释区分开来。
如果碰到巧妙的技巧、不易理解的流程、编程架构(construct)的漂亮使用方式或者是其他任何你想牢记的内容,务必要做笔记。如果你读不下去了,你也可以做个记录,提醒自己之后要回去再读看不懂的部分。
通过写下你的思绪,你实际上是在把那块代码变成你自己写的。慢慢地,你就会开始在工作中自然地用上新掌握的那些架构(construct)。
融汇贯通
学习编程,是一个反复读代码和写代码的持续过程。只要你愿意接触不同的风格、代码,最终你会形成自己的独特视角和思维