架構整潔之道--跟開源三方軟件劃清界限實戰(zhàn)案例

背景:

假設有一個開源/第三方的軟件模塊ModuleA,我們要基于其上做特性的擴展。我見過的較多的做法是二者的代碼實現(xiàn)揉在一起,這樣會導致一個問題。當ModuleA做升級或者ModuleA替換成另一款ModuleB的時候,需要有大量的代碼做Merge適配。這樣帶來的一個比較大的問題是,每次升級開源/三方軟件,極大概率會出現(xiàn)漏合錯合代碼的現(xiàn)象。(我所經(jīng)歷的項目中,有因為漏合入一行代碼,引入概率性問題,攻關一個月之久)。因此我們需要有一種方法,至少讓自研代碼和原始開源/三方軟件的代碼隔離開來,以插件式拼接的方式面對這種升級問題,而不是瑣碎的侵入式穿插修改。

案例:

下面我們就以一個例子為主線,講述一種在實戰(zhàn)中使用的案例。實實在在解決了上述痛點問題。
假設原生開源/三方軟件中有一個ModuleA,其中包含F(xiàn)unc1,和Func2兩個功能,見圖1。


圖1 ModuleA

假設此時,我們需要往ModuleA中增加一個功能FuncEx,以擴展ModuleA的邏輯從而滿足我們的需求。其中調(diào)用關系為Func1->FuncEx->Func2。見圖2


圖2 ModuleA擴展

圖2修改方法,其實已經(jīng)進入了本文講的問題痛點范疇,自研代碼和原生代碼糾纏在一起。我們想辦法把它解耦出來。ModuleEx作為一個獨立的模塊提取出來,只包含F(xiàn)uncEx功能,放在獨立的文件和目錄中,見圖3


圖3 提取ModuleEx

但此時出現(xiàn)了明顯的循環(huán)依賴。循環(huán)依賴倒不是關鍵問題,因為我上面說了,我們只是從代碼層面解耦開來,ModuleEx是無法脫離ModuleA單獨存在,從本質上來講,它只是附屬于ModuleA的一個片段。所以從編譯構建和運行的角度,它們還是一個整體,循環(huán)依賴屬于組件模塊內(nèi)部的依賴。

我們之所以需要繼續(xù)改造它,是因為,它不夠穩(wěn)定,設想一下,如果由于升級ModuleA,F(xiàn)unc2有變化(名稱變更或者實現(xiàn)邏輯變化需要找另一個函數(shù)替代),必然導致FuncEx跟著變化。而正如架構整潔之道中講的,好的架構需要保護自己的核心域不受影響。因此解決方法也是使用Bob大叔提供的方法,增加接口實現(xiàn)依賴倒置,從而也解決了循環(huán)依賴的問題。這樣從架構邊界上看,就變成了開源/三方依賴自研。見圖4

圖4 增加接口隔離

但實際上,這里還有一個問題,我們沒有辦法撼動開源/三方的ModuleA實現(xiàn)我們的接口Func2Inf。我們只能自己來做這件事情,畢竟受益的是我們自己。因此我們增加了ModuleAAdapter,來實現(xiàn)接口Func2Inf。見圖5


圖5 增加ModuleA的適配層

從圖5看,好像對ModuleA的依賴又變成了循環(huán)的,但其實不然。我們要把ModuleAAdapter看作ModuleA的一部分,則ModuleA和ModuleEx之間仍然是單向依賴。只不過ModuleAAdapter的實現(xiàn)由ModuleA的使用方來進行。ModuleAAdapter會隨著ModuleA的變化而變化,但是也僅限于ModuleAAdapter的變化,我們用維護ModuleAAdapter這一層的代價,保護了ModuleEx的穩(wěn)定性。

我們繼續(xù)演化,假設需要用ModuleB替代ModuleA,則ModuleB可能沒有辦法直接調(diào)用ModuleEx中的FuncEx,需要額外增加一點修改才能供ModuleB使用。這是一種典型的需要增加適配層的場景。因此,增加一層對ModuleEx的適配層。見圖6


圖6 增加ModuleEx的適配層

至此,整個問題的解決方案已經(jīng)講述完畢,最后,我們需要用不同的層次來看待這張圖,從代碼維護者的角度劃分和從依賴關系的角度劃分,會存在兩個架構邊界,見圖7。很多人可能糾結于維護邊界上的循環(huán)依賴而裹足不前,而本案例中,承認這種循環(huán)依賴的無害性,完美地解決了面臨的問題。

圖7 兩個邊界

有人會追問,這種場景下到底能不能設計成明確的單向依賴?我們通常意義上說的單向依賴,一般是組件間的依賴,它們之間應該有較少的接口依賴,往往比較容易做成易變的依賴穩(wěn)定的組件。即便有少量的循環(huán)依賴,也可以通過插入接口等方式實現(xiàn)依賴導致。

本文中的場景,更多的是同一組件內(nèi)的模塊間的依賴,理論上講,如果一個組件屬于同一團隊開發(fā),內(nèi)部模塊間的依賴,應該屬于正常依賴。但是由于本文的特殊場景,開源三方和自研特性的開發(fā)者不屬于同一個團隊,所以通過增加接口和適配層,讓變化局限在適配層這部分,從而最大限度保護核心業(yè)務邏輯不受影響。從編譯構建的角度看,它們其實仍然是一個整體,任何一塊都無法單獨構建運行,其實歸根結底還是存在依賴,只不過從開發(fā)的視角,代碼隔離開來了。但是把代碼隔離開來,正是本文場景中最迫切要解決的訴求。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容