源代码阅读总结(一) —— 基本方法

来源:互联网 发布:php需要学多久 编辑:程序博客网 时间:2024/06/07 02:40

        

        主要流程与方法: 


         STEP0.     了解系统的整体设计及概念
 
       优秀的系统通常会有一个优雅的整体设计, 由一组简洁的概念串联起来。 最好首先了解系统的整体框架和所基于的概念, 会对理解系统的构造与运行有一个总体的引导作用。 同时, 在设计自己的系统时, 也会从中获得很好的启发。 
 
       比如, Proxool 数据库连接池的设计与实现, 主要包括: 与数据库连接定义相关的 ProxyConnection 对象, 与数据源查询相关的 ProxoolDataSource 对象, 与连接池定义和管理相关的 ConnectionPool 对象和 ConnectionPoolDefinition 对象, 与弹性管理连接相关的 Prototyper 任务及 HouseKeeper 任务, 和相对应的线程 PrototyperThread, HouseKeeperThread。 这两个线程作为定时任务, 根据连接池配置定时扫描连接池,  创建新的连接或回收空闲连接。 以及全局管理者, 包括用于连接池注册的 ConnectionPoolManager 对象, 用于Prototyper 注册及任务启动的  PrototyperController,  用于 HouseKeeper 注册及任务启动的 HouseKeeperController 对象。 详见 《Proxool连接池解析(上)》 。 总之, 对象责任分配合理, 各司其职。

STEP1.    熟悉项目代码组织结构


       优秀的开源项目通常会有清晰而有层次化的项目组织结构。

       拿到项目源代码, 第一件事:概览项目组织结构, 弄清楚哪些主要模块(包),  各个模块(包) 的主要作用;各个模块(包) 下面有哪些主要类, 其作用如何。 这一步可以采用大胆猜测加API文档说明的方法来完成。 

       通常, 最顶层包往往包含与客户端使用直接相关的类和方法,  其下层的子包完成某个子功能或特殊模块。 此外, 优秀的项目代码通常会对系统所涉及的每个点都有一个较好的抽象, 使用一个小而简洁的类或接口来表达,  而不是混杂在大的类中。 哪怕只是一些简单的颜色字符串, 也会尽力用枚举来实现它。很多 Javaweb 项目默认采用 Controller - Service - (Dao, Http) - Model - Utils 的整体组织模式, 这进一步降低了阅读 Java 项目的难度。
 

STEP2.    确立目标,缩小范围


        在开始阅读源代码之前, 心里要定一个目标去驱动阅读。 比如说, 要探究 proxools 连接池有时获取不到数据库连接的问题,  或者弄清楚 extjs 分页控件 PagingBar & java 线程池 ThreadPoolExecutor 的内部实现。  先攻取一个小分支, 将阅读范围缩小到容易完成的程度。物理学家一上来也不是能一下子弄懂整个宇宙的。


STEP3.    找到切入点

       可以从外部行为切入。 阅读 API 文档,  理解其外部行为,弄清楚设计所针对的需求目标, 理解这些类或模块是如何协作完成整体目标的, 阅读(如果有的话)或者绘制出相应的协作设计图;  此外, 根据自己的开发经验和才智, 主动思考和大胆猜测:  如果是自己, 将会如何实现?  可能是通过何种方式来实现?
 
       还可以根据实际关注点切入。 比如, 使用 Proxool 连接池, 我更关心是如何获取数据库连接的, 就可以从 ProxoolDataSource.getConnection 方法入手, 详见 《Proxool连接池解析(中)》。 比如, 使用线程池, 通常会从 ThreadPoolExecutor 切入。 


STEP4.    锁定主要和核心的类与方法

       任何设计都会隐式或显式地有“关键角色” 与 “支撑角色” 的分工。阅读源代码并不是盲目漫无目的的行为, 而是要先锁定主要和核心的类与方法, 作为阅读的引路灯。比如, Proxool 的关键类的有: ProxoolDataSource, ProxyConnection, ConnectionPool, Prototyper, HouseKeeper , 支撑类有 ConnectionPoolManager , ConnectionPoolDefinition, PrototyperController, HouseKeeperController 等。 


STEP5:   标记主要流程, 绘制协作交互图

        跳过各种细节, 主要集中于弄清楚主要流程, 由哪些模块、类以及哪些方法参与, 标记、绘制协作交互图。 详见 《Proxool连接池解析(中)》。


STEP6:   分解细节,各个击破

        完成 STEP5 之后, 通常对该框架已经有了一个整体的理解, 虽然还有很多细节不清楚。 没关系! 开发框架为了追求灵活性和可扩展性, 相对比业务系统代码更难理解一些。 尤其是细节盘根错节,相互依赖影响。 将多种细节分解成多个小任务, 各个击破。 这一步需要反复多次地进行,  可能需要查阅很多知识点和接触一些“阴暗之处”,  才能逐渐抵达系统的“真相”, 最终作出对其优缺点的合理的综合评定。 
  

STEP7:  实践,再实践!
      
        可以先从一些小的组件、框架开始。 比如, Log4j , junit3,  java 集合框架, 这些都只涉及少量的概念、交互及设计模式, 稍微有点耐心都可以攻克下来;  接下来, 可以从实际使用的框架入手, 比如 Struts2, Spring , 弄清楚核心模块, 比如 struts2 MVC 和拦截器, SpringIOC ;  如果需要的话, 可以阅读 Java 并发库, 其难点并不在于交互, 而在于并发本身的复杂性。   



代码与设计模式:

       代码编写和程序设计都有一些模式可以遵循。 熟悉这些代码与设计模式对理解源代码也有很好的帮助。 比如, 面向对象系统中就通常有如下几种基本模式。
 
     1. 独立类
 
         独立类完成其独立的功能,会引用到其它类的方法, 但交互不会复杂。 例如 java.util.Arrays , java.lang.Integer 类;
 
     2.  支撑类
 
         支撑类用来表达一个小而简洁的抽象, 它通常不直接拿来用, 而是被其它类引用来构造更复杂的功能, 比如 java.util.AbstractMap$SimpleEntry
 
    3. 继承型交互关系
 
     继承型的交互关系遵循"接口-抽象类-具体类" 的模式: 接口规定行为规范, 抽象类完成用于定制的骨架实现, 具体类则实现具体完整的功能,可以直接拿来用。 例如 Map --> AbstractMap --> HashMap
 
     4. 委托型交互关系
 
      委托型交互遵循“封装或代理”模式,将一部分或全部的功能实现委托转发给其它类的实现, 可能会做一点封装或代理操作。例如, FutureTask 将 Callable 任务的执行委托给 Sync , 而 Sync 又继承于 AbstractQueuedSynchronizer。
 
      实际应用中, 通常是多种模式混合而成。在解决具体设计问题时, 又会应用到一些常见的设计模式, 比如单例模式、观察者模式、装饰器模式、代理模式、责任链模式等, 熟悉这些设计模式也是必要的, 详见《设计模式-可复用面向对象软件的基础》 一书。 


辅助手段: 

   1.    边阅读边注释

          对读过的地方做些必要的注释, 主要突出其用途,必要的实现细节;  可以边读边做些笔记。


   2.    搭建环境, 运行, 调试

          搭建好源码环境, 运行起来, 然后设置断点调试, 观察结果, 很好的跟踪方式;


   3.    从简单着手, 善于分解小任务

          对 Spring ,  Tomcat 的源代码无从下手?  从 JDK, Junit 看起;  看不懂 Extjs Grid 的代码? 从 Label, ComboBox  着手; 一个庞大的类看的吃力? 不妨将其分解成多个小任务, 各个击破。

         就像任何一门技艺一样, 阅读源代码的技能也要从基础一点一点的训练, 直到娴熟、炉火纯青的境界。


    4.    找点其它的事情做做

        阅读源代码可不是这世界最美妙的事情。 如果起初不是很适应的话, 可以先读若干函数, 然后做点其它事, 比如活动活动, 听听歌看看视频, 然后再回来阅读, 反复如此, 来逐渐增强这种耐心和适应感。        



心理考验: 


难以看懂的地方是挑战和提升的机会

      总会遇到看不懂,想不通的地方;  这时, 可能需要弥补下基础知识, 也可能学习到某种高级技巧, 一定不要放过这种学习机会。

   
耐心, 毅力,  攻城之战

     这是必过的槛, 必须攻克的城池。 有些艰难, 需要很大的耐心和毅力, 但是, 一旦攻下, 就为后面的前进打下很好的铺垫了。


           
           参考文章:   

           1.   如何阅读别人的源代码

               2.   如何阅读源代码 

               3.  如何看懂源代码