上一篇我们通过DSM来确定了核心对象并构建了抽象模型。
本篇是《如何高效阅读源码》专题的第八篇,我们来基于抽象模型来梳理核心流程。

本节主要内容:

  • 如何通过抽象模型来梳理核心流程

从类名和注释了解类的作用

上一篇的最后,我们得到了下面的抽象模型。

可以看到,最下面的三个类RunnerScheduler、RunnerBuilder和Statement,和其它的类没什么关系,我们可以暂时忽略它们。
从上面的调用关系和依赖关系,我们可以知道:

  • FrameworkMember是个抽象类

  • 其有两个子类FrameworkField和FrameworkMethod

  • FrameworkMember和TestClass都实现了Annotatable接口

  • TestClass调用了FrameworkField、FrameworkMethod两个类和MemberValueConsumer接口

线条说明:
白色虚线箭头
:关联关系,表现为箭头头部的类作为箭头尾部类的方法参数
白色实线箭头:组合关系,表现为箭头头部的类作为箭头尾部类的字段
蓝色实线箭头:继承关系,表现为箭头尾部的类继承了箭头头部的类
绿色虚线箭头:实现关系,表现为箭头尾部的类实现了箭头头部的接口
前面的文章中还有两种箭头
黄色虚线:注解依赖,即一个类使用了某个注解
红色实现:内部类,即一个类是另一个类的内部类

我们分别打开这几个类的源码(选中对应的类,按下F4)来阅读类上的注释,通过类名和类上的注释,我们可以了解到:

  • TestClass是「测试Class」的抽象。例如前面的PersonTest,它是PersonTest.class的抽象。

  • FrameworkField是测试类中的属性或影子属性(包装属性)

  • FrameworkMethod是测试类中的方法或影子方法(包装方法)

  • Annotatable只是统一了获取注解的接口

  • MemberValueConsumer用于收集对应的FrameworkMember的值

影子属性和影子方法是JUnit注释里的说法,我们暂时先这么称呼。结合前面的概念模型,猜测和Rule有关系。

构建初步流程

从类的功能,我们可以梳理出一个大概的流程:

  • TestClass对「测试Class」进行抽象

  • 将「测试Class」中的字段封装为FrameworkField

  • 将「测试Class」中的方法封装为FrameworMethod

  • 将field和method执行的结果存到MemberValueConsumer中

找出关键方法

从上面的流程,我们可以梳理出一些关键方法:

  • TestClass应该会接收一个Class类型的参数来构建实例。

  • 同时TestClass应该有方法来解析Class里的field和method,并分别构建为FrameworkField和FrameworkMethod

  • TestClass中应该有方法来将field和method的值设置到MemberValueConsumer中

  • FrameworkField应该有方法来获取自身的值

  • FrameworMethod也应该有方法来获取自身的值

通过IDEA的Structure视图,我们可以很快的定位到对应的方法:

  • TestClass中有一个有参的构造方法,接收Class类型的参数

  • 通过scanAnnotatedMembers方法扫描方法和属性,来构建FrameworkField和FrameworkMethod

  • 注意最后两行,makeDeeplyUnmodifiable方法是干嘛用的呢?看名字是将对象转换为不可变的,为什么要转换成不可变对象呢?

转换为不可变对象无非两种情况:

  • 不希望对象被修改,特别是多线程情况下,可能会有不可预期的修改

  • 没有修改,也就可以安心的使用多线程,不用考虑锁的问题。

不过由于此方法非核心方法,我们就暂时不管了。并不影响流程梳理。此问题可以留到后面再来思考答案。
我们继续看scanAnnotatedMembers方法:

  • 分别遍历方法和属性,将其加入到对应的Map中

  • 注意这里的MethodSorter,它对方法进行了排序

另外可以发现TestClass中有两个方法collectAnnotatedFieldValues和collectAnnotatedMethodValues,从名字就可以了解到,这两个方法是用于获取字段和方法的值,并设置到了MemberValueConsumer中。

至此,我们也就梳理出了核心流程,虽然还有几个疑问,但是没关系,我们后面慢慢来解答。

总结

本文阐述了基于抽象模型来梳理核心流程的方法,并通过JUnit来演示具体的梳理核心流程的方法。
下文将对核心流程绘制流程图图,同时将核心流程图和我们的概念模型及抽象模型进行整合,绘制出一个更完整的执行流程图。

无需debug,通过抽象模型快速梳理代码核心流程的更多相关文章

  1. TinyHTTP代码核心流程

    TinyHTTPd是一个超轻量型Http Server,使用C语言开发,全部代码不到600行 研究HTTP服务器,为了更好的造轮子,看了TinyHTTPd代码,对逻辑处理画个简单流程图(不含底层)

  2. character-RNN模型介绍以及代码解析

    RNN是一个很有意思的模型.早在20年前就有学者发现了它强大的时序记忆能力,另外学术界以证实RNN模型属于Turning-Complete,即理论上可以模拟任何函数.但实际运作上,一开始由于vanis ...

  3. 中文NER的那些事儿1. Bert-Bilstm-CRF基线模型详解&代码实现

    这个系列我们来聊聊序列标注中的中文实体识别问题,第一章让我们从当前比较通用的基准模型Bert+Bilstm+CRF说起,看看这个模型已经解决了哪些问题还有哪些问题待解决.以下模型实现和评估脚本,详见 ...

  4. 动态生成一个设定好特殊样式的Tlabel,快速生成代码

    动态生成一个设定好特殊样式的Tlabel,快速生成代码: 1.自己先在可视化界面设定一个Label,像这样: 2.选择label,快捷键ctrl+C 复制,粘贴带代码编辑器去,会生成一段这样的窗体代码 ...

  5. 利用sublime的snippet功能快速创建代码段

    在前端开发中我们经常会输入相同的一些基本代码,例如常用的jquery引用,bootstrap框架,cssreset等等,如果每次使用时在复制粘贴感觉很麻烦,这里介绍一种更为简洁的方法 利用sublim ...

  6. Visual Studio 内置快速生产代码简写集合

    工作之余,整理了一下,Visual Studio 里面的快速生产代码缩写集合,这个拿出来分享想一下,希望对您有所帮助. 文件下载地址:VS内置生产代码缩写集合文档.rar 首字母 简写 生成代码 a ...

  7. live Templates 活动模板. 配置完之后,就可以快速编码-代码块

    配置:live Templates 活动模板. 配置完之后,就可以快速编码-代码块. 输入startflask敲回车:   就会生成代码:   怎么做到的呢? 如下:   注意第七步: 原本不是cha ...

  8. Emmet快速编写代码

    Emmet快速编写代码 ★div → <div></div>, span → <span></span> ★CSS选择器 ​ 给标签指定id选择器 di ...

  9. 前端html、CSS快速编写代码插件-Emmet使用方法技巧详解

    前端html.CSS快速编写代码插件-Emmet使用方法技巧详解   Emmet的前身是大名鼎鼎的Zen coding,如果你从事Web前端开发的话,对该插件一定不会陌生.它使用仿CSS选择器的语法来 ...

随机推荐

  1. Three.js 火焰效果实现艾尔登法环动态logo 🔥

    声明:本文涉及图文和模型素材仅用于个人学习.研究和欣赏,请勿二次修改.非法传播.转载.出版.商用.及进行其他获利行为. 背景 <艾尔登法环>是最近比较火的一款游戏,观察可以发现它的 Log ...

  2. OpenCV基础_二

    阈值和平滑处理 cv2.threshold()二值化函数 ret,thresh = cv2.threshold(src, thresh, maxval, type[, dst]) 参数 src:所要进 ...

  3. matplotlib字体

    matplotlib官方文档 https://matplotlib.org/stable/gallery/index.html 使用以下代码查看 import matplotlib.pyplot as ...

  4. 为什么JVM要用到压缩指针?Java对象要求8字节的整数倍?

    前言 前两天在一个帖子中看到一道面试题: 堆内存超过32G时,为什么压缩指针失效? 之前没有了解过这方面的知识,于是开始google起来,但当我翻看了不下一页的帖子,我都仍然没有搞懂,因为好多答案给我 ...

  5. 什么是BASH?

    BASH是Bourne Again SHell的缩写.它由Steve Bourne编写,作为原始Bourne Shell(由/ bin / sh表示)的替代品.它结合了原始版本的Bourne Shel ...

  6. spring源码-ioc容器周期

    Spring容器的refresh 创建刷新:   1-prepareRefresh刷新前的预处理: initPropertySources 初始化一些属性配置,原来是空的,子类自定义的属性设置方法 g ...

  7. JavaScript中call如何使用?

    一句话概括:call是函数才能调用的方法.作用是改变属性环境.有点绕,举个例子. 此处代码是在window环境下声明的变量和函数. var name='王延朋'; var age=18; var ex ...

  8. redis 过期键的删除策略?

    1.定时删除:在设置键的过期时间的同时,创建一个定时器 timer). 让定时器在键 的过期时间来临时,立即执行对键的删除操作. 2.惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的 ...

  9. Thymeleaf+Spring使用自己的工具类

    第一种.提供思路,继承SpringStandardDialect,重写getExpressionObjectFactory方法,设置expressionObjectFactory的实际对象,并在Tem ...

  10. 小程序完整对接 pingpp支付

    小程序完整对接 pingpp支付 有几个先要条件: 小程序需要企业认证且开通支付功能,个人认证是无法使用支付功能的(小程序微信支付官网) pingpp 本身接入的企业服务器(即商户服务器)并不强制要求 ...