上一篇我們通過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來演示具體的梳理核心流程的方法。
下文將對核心流程繪製流程圖圖,同時將核心流程圖和我們的概念模型及抽象模型進行整合,繪製出一個更完整的執行流程圖。