背景
我們?cè)谧鲆粋€(gè) application engine 角撞,涉及到今天要講的內(nèi)容有兩部分,運(yùn)行時(shí)環(huán)境和對(duì)象解析庫(kù)。運(yùn)行時(shí)環(huán)境顧名思義就是用戶代碼運(yùn)行時(shí)的環(huán)節(jié)屠升,類似于 jvm ,而對(duì)象解析庫(kù)就類似于 javac + classloader 费奸。這兩部分基本上同時(shí)進(jìn)行開(kāi)發(fā)的弥激,也就是說(shuō)在運(yùn)行時(shí)環(huán)境還沒(méi)確定已何種方式使用對(duì)象解析庫(kù)時(shí),對(duì)象解析庫(kù)就開(kāi)始了設(shè)計(jì)和開(kāi)發(fā)的工作愿阐。而我就是那個(gè)設(shè)計(jì)和開(kāi)發(fā)的人微服。
需求
因?yàn)楫?dāng)時(shí)運(yùn)行時(shí)環(huán)境還在設(shè)計(jì)和開(kāi)發(fā)中,而我拿到的需求是寫一個(gè)較為通用的解析庫(kù)缨历,負(fù)責(zé)解析對(duì)象文件并裝配上一些預(yù)定義的方法以蕴。
設(shè)計(jì)
因?yàn)閷?duì)象具體的行為是由運(yùn)行時(shí)環(huán)境來(lái)執(zhí)行的,而解析庫(kù)又要負(fù)責(zé)解析對(duì)象應(yīng)該具備哪些行為辛孵,并把相應(yīng)的行為定義上去丛肮。所以我就定義了一個(gè)接口來(lái)表示所有的原子行為,然后解析對(duì)象并裝配上行為(有可能是原子行為或原子行為的組合)魄缚。當(dāng)運(yùn)行時(shí)環(huán)境需要使用解析庫(kù)時(shí)宝与,實(shí)現(xiàn)原子行為的接口即可。
選擇
解析庫(kù)是在運(yùn)行時(shí)之前開(kāi)發(fā)完成的冶匹,之后我又去做了別的事情习劫。等到運(yùn)行時(shí)需要使用解析庫(kù)時(shí),才發(fā)現(xiàn)兩個(gè)設(shè)計(jì)人員的概念模型是不一致的嚼隘。在我的想法里诽里,解析庫(kù)是可以運(yùn)行在不同運(yùn)行時(shí)環(huán)境的,所以我定義了一組原子行為由運(yùn)行時(shí)環(huán)境來(lái)實(shí)現(xiàn)飞蛹。而設(shè)計(jì)運(yùn)行時(shí)環(huán)境的人想法是由環(huán)境對(duì)外提供一個(gè)標(biāo)準(zhǔn)方法谤狡,解析庫(kù)通過(guò)這個(gè)方法實(shí)現(xiàn)所有對(duì)象的行為。
可以說(shuō)解析庫(kù)就是為運(yùn)行時(shí)環(huán)境而生的卧檐,所以解析庫(kù)要改墓懂。那么有兩個(gè)選擇擺在我面前。
重構(gòu)解析庫(kù)
寫一個(gè)適配器泄隔,適配器使用環(huán)境提供的標(biāo)準(zhǔn)方法實(shí)現(xiàn)解析庫(kù)借口定義的原子行為
其實(shí)我的優(yōu)先選擇是重構(gòu)拒贱,但考慮到時(shí)間因素,最后采取了第二個(gè)方案。
重點(diǎn)來(lái)了
適配器很快就寫完了逻澳,整個(gè)系統(tǒng)也通過(guò)跑了幾個(gè)簡(jiǎn)單的 demo 得到了驗(yàn)證闸天。于是我又去忙別的事情了。之后的莫一天斜做,有一個(gè)驗(yàn)證別的模塊的功能跑不通苞氮,于是懷疑是解析庫(kù)的 bug 。業(yè)務(wù)邏輯是這樣的:
環(huán)境先提供了一個(gè)標(biāo)準(zhǔn)方法來(lái)解析一批對(duì)象瓤逼,這時(shí)候?qū)ο蟮谋憩F(xiàn)是正常的笼吟。
環(huán)境又提供了另一個(gè)標(biāo)準(zhǔn)方法來(lái)解析另一批對(duì)象,這時(shí)候這批對(duì)象的表現(xiàn)也是正常的霸旗。
接著環(huán)境再調(diào)用第一批對(duì)象時(shí)就表現(xiàn)出了異常贷帮。
其實(shí)碰到這樣的問(wèn)題,最簡(jiǎn)單的方式就是在解析庫(kù)里加一個(gè)這種場(chǎng)景的測(cè)試诱告,就知道是不是解析庫(kù)的問(wèn)題了撵枢。然而當(dāng)時(shí)我的第一選擇是通過(guò)邏輯推斷來(lái)定位 bug ,因?yàn)楫?dāng)解析完成時(shí)精居,對(duì)象和方法的關(guān)系就已經(jīng)綁定了锄禽,如果起先沒(méi)有問(wèn)題,而后來(lái)出現(xiàn)問(wèn)題靴姿,那很有可能是這個(gè)方法被變更了沃但。所以基本上花了十幾分鐘來(lái)檢查環(huán)境的代碼和 demo 的代碼可能存在什么問(wèn)題。然后又大概花了五分鐘來(lái)說(shuō)明我推斷的理由佛吓,所以差不多浪費(fèi)了二十分鐘后宵晚,我終于選擇增加一個(gè)場(chǎng)景測(cè)試來(lái)判斷問(wèn)題。而加入這個(gè)測(cè)試只花了不到五分鐘维雇,然后兩分鐘后就定位到問(wèn)題了坝疼,最后修改的代碼在十行內(nèi),當(dāng)然修改方案想的時(shí)間比較久谆沃。
事后下班回家的路上我在想當(dāng)時(shí)的選擇是否正確?在我聽(tīng)完 bug 描述之后仪芒,幾乎本能的要寫一個(gè)測(cè)試來(lái)證明這不是解析庫(kù)的問(wèn)題唁影,然而后來(lái)我的選擇卻不是這個(gè),我試著分析自己的心理掂名。首先無(wú)論引起 bug 真實(shí)的原因是什么据沈,這個(gè)測(cè)試都是要加的。但如果加了測(cè)試以后證明不是解析庫(kù)的 bug 饺蔑,問(wèn)題還是沒(méi)有解決锌介,需要繼續(xù)尋找問(wèn)題的所在。而當(dāng)時(shí)聽(tīng)完描述之后我自信的認(rèn)定這不可能是解析庫(kù)的問(wèn)題,所以想要跳過(guò)寫測(cè)試的那一階段孔祸,直接進(jìn)入找問(wèn)題的階段隆敢。
這里看上去是我過(guò)分自信的問(wèn)題,其實(shí)自信不是問(wèn)題崔慧,而是我的判斷依據(jù)出了問(wèn)題拂蝎。在我的概念模型里對(duì)象和方法的關(guān)系在解析時(shí)就做了綁定,而解析庫(kù)在解析時(shí)實(shí)際的行為是綁定對(duì)象和原子行為實(shí)現(xiàn)類的關(guān)系惶室。運(yùn)行時(shí)雖然定義了兩個(gè)不同的方法温自,但原子行為實(shí)現(xiàn)類卻是一個(gè)(原先的概念模型里一個(gè)環(huán)節(jié)一個(gè)實(shí)現(xiàn)類是合理的)。問(wèn)題就在于我腦子里的概念模型與系統(tǒng)實(shí)際的概念模型不一致皇钞,所以才導(dǎo)致我認(rèn)為不會(huì)出問(wèn)題的地方出了問(wèn)題悼泌。這其實(shí)也是當(dāng)初我優(yōu)先選擇重構(gòu)解析庫(kù)的原因。