源码阅读系列:源码阅读方法

一.前提条件

1.纯熟扎实的语言基础

??如果你学java,却对反射、泛型、注解一直半解,还是不要去读什么框架了,回去把java基础打扎实反而对你自身更有益。

2.UML能力

??在软件工程中,UML在软件的不同生命周期阶段扮演着非常重要的角色,没有好的UML水平,面对大型的项目源码会束手无策。

3.对业务的理解

??如果你要阅读的项目业务性比较强,事先对业务有一定的了解是必须的。

4.设计模式、重构的掌握

??编程语言什么的没什么好说。着重提一个:设计模式由于Android源代码用到各种各样的设计模式,如果不会设计模式,将会大大降低你的阅读理解速度。

5.对编码规范和约定的深刻理解、认识

??意义重大的编码工作,或大型、有组织体制之下的项目,都会采纳一套编码规范、指导原则或约定。目标是增强代码的可靠性、易读性和可维护性。

??懂得这套规则,可能一个类名、一个方法名就可以帮助您了解整个类或方法的功能,或者约定俗成的分隔,都会对你的思路有一定的辅助。

6.知道你的目的是什么

??你读这个代码是为了解决什么问题,不是为了读而读,你觉得它哪些地方设计的优秀?

二.总-功能了解

??了解项目功能(Sample 和文档)以及主要分为几个大块。另外明白你的需求,比如 PullToRefresh 的下拉实现。

??这个代码是为了满足什么需求而设计的,是为了解决什么问题?有何优点,有何缺点?加入让你来设计解决这个问题,你会从哪些方面着手?你会遇到什么问题?你会怎么解决这些问题?然后看代码相互印证学习。

1.大家可以自己先想想如果是自己会怎么去实现这个项目,或许看源码过程中会发现思想碰撞。

2.如果时间比较紧,可以先 Google 一些该项目相关的文档结合代码看看,帮助快速掌握,不过网上分析文章参差不齐,需谨慎。

三.总-总体设计

关键字:层次(分层)、常见模式对应、流程与关系、自上而下、必须找好切入点、模块、组件

??要了解一个系统,最好是采取由上至下的方式。先试着捕捉系统架构性的观念,不要过早钻进细节,因为那通常对于你了解全貌,没有多大的帮助。阅读程式码不需要从第一行读起,我们的目的并不是在于读遍每一段程式码。

??阅读程式码时,多半会采取由上而下,抽丝剥茧的方式。透过记录层层展开的树状结构,程式人可以逐步地建立起对系统的架构观,而且可以依照需要的粒度(粒度) ,决定展开的层次及精致程度。

??重视架构胜于细节,过早钻入细节,对你理解全貌无益。

??从系统的源代码库洞悉系统的架构,不是一件容易做到的事。然而,在识别出重要的架构元素之后,那么,浏览整个系统,了解系统的结构和属性,以及规划增加、修改和重构活动都会变得更为容易。这是因为,一旦提取系统的构架性特征,我们就立即与系统的创建者共享一套语义丰富的词汇。另外,对系统架构的理解,能够帮助我们了解交互的类型、通信模式和代码结构。

1.输入

源码、别人的分析文档

2.工具

○ 画总体图理清逻辑

??根据使用流程来梳理(非常重要),即先看如何调用的,然后根据调用的流程走一走。

??画类图(只画出重点的方法和类即可),可以主次分明,也可以是草图,可以帮助你快速了解内部的代码流程。使用UML里的类图建立静态结构,分析出类与类之间的关系。

??用例图,类图,时序图,随手画的图都可以

??边看代码边画图,当你把所有的代码都能转化成设计图时,你必然对代码足够了解。

??画图,对于复杂逻辑的程序

??整个库分为哪些模块及模块之间的调用关系。如大多数图片缓存会分为 Loader 和 Processer 等模块。

○ 提问

1.系统如何初始化?

对于app来说,对完整 App 来说就是 Manifest 找到入口 Activity,对于工具库从调用接口中判断入口类。然后在 IDE 中一步步深入即可。

PS:一般不错的开源项目规范都比较好,类、函数、变量从名字上就可以了解作用,所以如果需要快速掌握原理的话看觉得是重点的函数即可。

2.与这个系统相接的其他系统(或使用者)有那些,而相接的边界又是什么?

3.系统如何反应各种事件?

4.系统如何处理各种异常及错误?

○ 取经以往架构经验

设计模式

??一般成熟的项目中包含了大量的设计模式和架构思想。

架构模式

??大规模的软件开发工作必须使用一种合适的架构来构造所要创建的系统,控制其复杂性。这个架构一般制定系统的结构,控制的处理方式,以及如何对系统各个组成部分进行模块分解。大型的系统还可能会从框架、设计模式和特定领域的架构中汲取灵感,重用他们架构上的思想。

在更大粒度上,大型系统的代码常分解为对象模块库,可重用组件,甚至独立的集成。你也可以尝试从组件、库、模块的角度来考虑项目的架构。

我们分析的许多系统都遵循一种简单的“主程序和子例程”结构。其他的系统采用更为复杂的架构结构,来组织它们的各个构成子系统。常见的、重要的结构可以归类为少数迥然相异的架构类型:集中式储存库、数据流、面对对象或分层架构。这些架构类型常结合成一个层次结构,用来控制大型系统的复杂性。一个系统可以同时展示多种不同的架构类型,以不同的方式检查同一系统,分析系统的不同部分,或使用不同级别的分解,都有可能发现不同的架构类型。这块可以读一点架构的书籍。

元素封装:组件、模块、命名空间、对象、库。

○ 动手记笔记

??这里正式开始代码分析,分析过程中如果脑子记不住,多动手记下主要类、函数等作用

??对于框架而言,大概看一下API列表,找到重要的、自己感兴趣的API。

○ 项目的组织

??我们可以通过浏览项目的源代码树——包含项目源代码的层次目录结构,来分析一个项目的组织方式。源码树常常能够反映出项目在架构和软件过程上的结构。考虑apache web服务器的源代码树。

??不要被庞大的源代码集合吓到,他们一般比小型的专门项目组织的更出色。尽管看上去非常庞大,但是找到特定工具的源代码依旧轻而易举。

3.输出

总体图示、遇到的问题清单及源码阅读笔记

四.分-详细设计:深入关心的细节

1.输入

源码、别人的分析文档、理清架构的输出

2.工具

○ 定位

??找到需要了解的具体功能实现模块。工具方法也可如上。

??根据关键字快速搜索阅读,自己需要看的部分的代码。

??一旦定位到目标代码,就针对他进行研究分析,忽略不相关的其他部分。这是一种必须要掌握的技能。如果您觉得在原来的上下文中,理解代码很困难,就将它复制到一个临时文件中,删除所有不相关的部分。这个过程的正式名称是切片。

android中,快速定位感兴趣的功能在 find in path / find usages 的基础之上,更多的 command + F 页面内查找,一般基本定位后都难逃我们的掌心,方法其实都很简单。 https://drakeet.me/quickly-locate-the-function-code

○ 修改并运行代码&debug

??有时,您阅读的代码可能来自于对您来说完全陌生的环境(计算机语言、操作系统或API)。熟悉了编程和基本的计算机科学概念后,大多数情况下,您能够通过源代码来了解新环境的基本情况。但要注意从小型的程序开始阅读;不要立即陷入对大型系统的研究中。编译研究的程序并运行它们。这样您就可以得到即时的反馈,了解代码预想的工作方式,同时还可以获得成就感。下一步就是主动的修改代码来检验您对代码的理解是否正确,再次强调,要从小的改动做起,逐渐增大它们的范围。通过积极的介入现实的代码,您能够快速的从中了解到新环境的一些基本情况。当您认为已经掌握了他们之后,要考虑投入一些努力(可能还需要投入一些资金),采取更有组织方式来学习该环境。阅读相关的书籍、文档或手册,或者参加培训课程,这两种方式相互补充。

??另一种积极地阅读现有代码(作为文献)的方式是改进它。与其他文字作品相比,软件代码是活的人工制品,他们总是被不断的改进。如果代码对您或您的团体有价值,请考虑如何来改进它。者可能涉及到使用更好的设计或算法、为某些代码编制文档,或增加功能。开放源码项目中的代码常常没有很好地编制文档,请将您对代码的理解应用到改进文档上。在现有的代码上工作时,请与作者和维护人员进行必要的协调,以避免重复劳动或因此产生厌恶情绪。如果您的更改更为强壮,则考虑申请成为一个并发式版本控制系统的提交者。

??有疑问了运行一下程序,或者修改一下运行,看看结果和你预期的一致不,证明/否定你自己的想法。

??栈帧清晰的帮我们列出了方法的调用流程,是阅读源码非常主要的工具。

○ 代码执行流程分析(结合栈帧分析更佳)

??一般阅读非自己写的代码都是关注某一部分代码块,比如某一条流程线。如果这份代码让你看的眼花缭乱,条件允许的话最有效地方法就是进行debug调试。一步一步的跟进调试会让你很流畅的根据代码的执行步骤了解代码的逻辑。如果无法debug调试的话,我会从最浅层的代码层入手,理清思路,先不去关注深层的逻辑或者函数,需要使用到得时候在去仔细查看。

??当你在阅读一段程式码时,或许可以试着转换自己的立场,从旁观者的角度转换成为写作者的心态,揣摩原作者的心理及处境。当你试着设身处地站在他的立场,透过他的思考方式来阅读,追踪他所写下的程式码,将会感觉更加流畅。

??对需要的具体的代码进行详细分析。搞清变量的意义和关联关系,搞清实现的逻辑和算法。好的代码,此处最需要注释。

○ 重复阅读,注意注释(尤其是英文注释)加todo阅读

??即使读过的代码,还需要再读读,因为可能遗漏了很多优秀的设计地方,很多地方当时也可能理解失误

??如果你阅读过开源代码,你会看到,一块代码,可能有超过一半的程序代码是法律信息与管理信息,如版权信息、许可信息和程序版本标识符。大型的组织有序的系统通常都会提供此类信息,同时还会归纳具体程序或模块的功能。这是可以直接略过的部分。

○ 提问法分析

??遇到不知为何引入的,问自己,为啥引入,然后通过这个东西看他在哪里使用到了

○ 画局部细节图:三种图足够你看懂任何android代码了

  • ER图:实体类的关系 对于复杂的数据结构。使用抓包工具帮你分析实体的类型和对应关系。
  • 页面布局图:布局结构梳理 看android项目除了类图之外,整个页面的布局结构反而有的时候比类图还要重要 毕竟android属于前端技术
  • 类图.

○ 聚焦,列出重点关注的点,由点深入&切片

??要一下子把所有模仿都理解透,是件比较困难的事情。可以分模块学习

??在具体看一个类的时候,最好首先识别出重要的组成部分。就我们的情况来说,重要组成部分是全局变量和重要的方法。

??要了解一个函数的功用,可以使用下面的策略。

  • 猜。基于函数名
  • 阅读位于函数开始部分的注释
  • 分析如何使用该函数
  • 阅读函数体的代码
  • 查阅外部的程序文档

??切片:在推导程序结构的细节时,一个有价值的概念性工具就是切片。非正式的,您可以将程序片看做能够影响变量的值的一段程序。

○ 化整为零

3.输出

局部图示,问题清单及源码阅读笔记

五.回顾

  1. 解决你在阅读中遇到的问题
  2. 回头复习一下前几个阶段做的事情
  3. 提出改进意见你能否提出改进意见
  4. 设计得优秀的地方
  5. 总结成文档,最好写成博客,推荐一个目录结构。

    项目介绍

    特点

    简单用法(一个demo)

    总体设计

    类图

    流程分析:栈帧+时序图

    核心模块分述

    阅读体会&优缺点&改进意见

六.参考资料

https://www.zhihu.com/question/19759722

《代码阅读方法与实践》

时间: 2024-10-12 11:33:01

源码阅读系列:源码阅读方法的相关文章

【源码阅读系列】JDK 8 ConcurrentHashMap 源码分析之 由transfer引发的bug

不阅读源码就不会发现这个事儿 前段时间在阅读ConcurrentHashMap源码,版本JDK 8,目前源码研究已经告一段落.感谢鲁道的ConcurrentHashMap源码分析文章,读到文章,感觉和作者发生了一些交流,解答了很多疑惑,也验证了一些想法.鲁道在简书的addCount分析文章点这里 (文章底部的评论中就有这篇文章发酵的原由).鲁道还有其他ConcurrentHashMap源码分析的系列文章,在简书.掘金都有分布,感兴趣的同学可以进一步追踪. 推完文章,回到本篇的主题"阅读源码&qu

【Dubbo源码阅读系列】服务暴露之远程暴露

引言 什么叫 远程暴露 ?试着想象着这么一种场景:假设我们新增了一台服务器 A,专门用于发送短信提示给指定用户.那么问题来了,我们的 Message 服务上线之后,应该如何告知调用方服务器,服务器 A 提供了 Message 功能?那么我们是不是可以把目前已提供的服务暴露在一个地方,让调用方知道某台机器提供了某个特定功能?带着这样的假设,我们今天就来聊聊 Dubbo 服务暴露之远程暴露!! 服务远程暴露 先回顾一下上篇文章,上篇文章我们聊到了 ServiceConfig 的 export() 方

【Dubbo源码阅读系列】之远程服务调用(上)

今天打算来讲一讲 Dubbo 服务远程调用.笔者在开始看 Dubbo 远程服务相关源码的时候,看的有点迷糊.后来慢慢明白 Dubbo 远程服务的调用的本质就是动态代理模式的一种实现.本地消费者无须知道远程服务具体的实现,消费者和提供者通过代理类来进行交互!! 一.JAVA 动态代理 简单看一段代码回顾一下动态代理: public class MyInvocationHandler implements InvocationHandler{ private Object object; publi

angular源码阅读的起点,setupModuleLoader方法

angular源码其实结构非常清晰,划分的有条有理的,大概就是这样子: (function(window,document,jquery,undefined){ //一些工具函数 //EXPR 编译器 自执行 //setupModuleLoader方法,公司内部的框架是vxsetup方法,(只是定义,没有调用) //moduler方法() //angular初始化方法,公司内部的框架是vxinit方法 //bootstrap //createInjector //一系列指令,服务,过滤器等指令

Spring源码阅读系列总结

最近一段时间,粗略的查看了一下Spring源码,对Spring的两大核心和Spring的组件有了更深入的了解.同时在学习Spring源码时,得了解一些设计模式,不然阅读源码还是有一定难度的,所以一些重要的设计模式简单的做了阐述.同时还会简单的加入一些GOF中提到的设计原则.Spring的源码阅读系列,也暂告一段落.下面是就带你走进Spring世界: Spring系列的引子 1)Spring WebApplicationContext初始化与消亡 这一节帮我们了解Spring是如何初始化WebAp

如何阅读Java源码 阅读java的真实体会

刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 说到技术基础,我打个比方吧,如果你从来没有学过Java,或是任何一门编程语言如C++,一开始去啃<Core Java>,你是很难从中吸收到营养的,特别是<深入Java虚拟机>这类书,别人觉得好,未必适合现在的你. 虽然Tomcat的源码很漂亮,但我绝不建议你一开始就读它.我文中会专门谈到这个,暂时不展开. 强烈

手把手带你阅读Mybatis源码(三)缓存篇

前言 大家好,这一篇文章是MyBatis系列的最后一篇文章,前面两篇文章:手把手带你阅读Mybatis源码(一)构造篇 和 手把手带你阅读Mybatis源码(二)执行篇,主要说明了MyBatis是如何将我们的xml配置文件构建为其内部的Configuration对象和MappedStatement对象的,然后在第二篇我们说了构建完成后MyBatis是如何一步一步地执行我们的SQL语句并且对结果集进行封装的. 那么这篇作为MyBatis系列的最后一篇,自然是要来聊聊MyBatis中的一个不可忽视的

《STL源码剖析》---stl_tree.h阅读笔记

STL中,关联式容器的内部结构是一颗平衡二叉树,以便获得良好的搜索效率.红黑树是平衡二叉树的一种,它不像AVL树那样要求绝对平衡,降低了对旋转的要求,但是其性能并没有下降很多,它的搜索.插入.删除都能以O(nlogn)时间完成.平衡可以在一次或者两次旋转解决,是"性价比"很高的平衡二叉树. RB-tree(red black tree)红黑树是平衡二叉树.它满足一下规则 (1)每个节点不是红色就是黑色. (2)根节点是黑色. (3)如果节点为红色,则其子节点比为黑色. (4)任何一个节

《STL源码剖析》---stl_iterator.h阅读笔记

STL设计的中心思想是将容器(container)和算法(algorithm)分开,迭代器是容器(container)和算法(algorithm)之间的桥梁. 迭代器可以如下定义:提供一种方法,能够依序寻访某个容器内的所有元素,而又无需暴露该容器的内部表达方式. 在阅读代码之前,要先了解一个新概念:Traits编程技法 template <class T> struct MyIter { typedef T value_type //内嵌型别声明 T *ptr; MyIter(T *p = 0

阅读php源码须知

阅读php源码须知 一份好的源代码具有自己的一套设计思路和设计模式,所以在看某个产品之前就要做好心理准备,可以把自己的经验和这些产品做对比,但千万别一直用自己的思维去评判. 一句话就是“以学习和批评的辩证思想去看待”. 好吧!我们就开始吧!www.kyitjy.com 那具体的我们怎样去阅读一份php的源代码呢? 先把源代码安装起来,结合它的文档和手册,熟悉其功能和它的应用方式. 浏览源代码的目录结构,了解各个目录的功能. 经过以上两步后相信你对这个开源的产品有了一个初步的了解了,那现在就开始分