前端:組件、插件、模塊、子應用、庫、框架等區別


組件、插件、模塊、子應用、庫、框架等概念辨析

網上有許多講組件化、模塊化等概念的文章,但大家一般都是將這兩個概念混為一談的,並沒有加以區分。而且實際上許多人對於組件、插件、模塊、子應用等概念的區別也不甚明了,甚至於許多博客文章專門解說這幾個概念都有些謬誤。
之前已經寫了一篇文章專門對組件和模塊兩個概念進行辨析,現在我們對於更多的概念在更高的層次上進行辨析。
想分清這幾個概念我覺得結合一下軟件的漸進式開發場景更容易理解。但是下面的篇幅會比較長,所以按慣例還是先說結論,不耐煩的同學可以先看:

1.概念區別

  • 組件:代碼重用,功能相對單一或者獨立,無統一接口。組件化開發的成果是基礎庫和公共組件。
  • 插件:近乎組件,有統一接口
  • 模塊:高內聚,松耦合,功能相對復雜,有多個統一接口。模塊化開發的基礎是框架。
  • 子系統:高於模塊,需要生命周期管理。子系統開發的基礎是容器。

1.1.組件和插件

插件的概念比較形象,一般存在一個“插拔”過程,所以要求可插拔的插件有一個相同的接口(這里所說的接口只是概念上的接口,即調用方法及參數等)。而組件是不存在這個相同接口的。
拿我們最常見的網絡請求功能舉例,無論哪種開發語言,github上可能都有多種網絡請求組件,那么對於一個項目而言,從一個網絡組件ComponentA切換為另一個網絡組件ComponentB是基本無法做到調用方法不改動的。
而如果把網絡請求組件插件化,即在組件外層抽象一層統一化的調用接口NetworkInterface,然后將當前使用網絡請求組件ComponentA包裝成實現該接口的網絡請求插件PluginA。那么如果以后需要將使用的ComponentA切換為ComponentB,那么只需要將ComponentB包裝成PluginB並插入到應用中即可。實際調用時,業務代碼還是調用NetworkInterface,不用做任何修改。
從上面這個例子我們可以看出,插件和組件的實質區別就在於通過統一接口隔絕業務代碼對於組件的直接依賴,這也是我們常聽到的所謂的“項目開發時應該把第三方組件封裝一下再用”。

1.2.組件和模塊

兩者的實質區別在於:組件化開發是縱向分層,模塊化開發是橫向分塊。
所以,模塊化並沒有要求一定組件化,就是說進行模塊化拆分時你可以完全不考慮代碼重用,只是把同一業務的代碼做內聚整合成不同的模塊。只不過這樣得到的成果相對簡單,我們一般不會這樣而已。

組件化就比如項目中公共的alert框,它的出現其實是基於代碼復用的目的,所以我們把它封裝,並給多個地方使用。而模塊化就比如一個資訊列表界面,它本身可能只在一個地方使用,沒有復用的需求,但我們也要把它封裝成模塊,這是高內聚的要求,我們不應該把資訊相關的代碼在項目中放得到處都是。
但像這樣的簡單模塊只是輕模塊,統一接口較少。而統一定義的接口越多,其實和主應用的耦合就越高,也便是重模塊。
而路由就是解決高耦合問題的,不過耦合問題不是模塊化開發的需求,只不過我們一般都會在這個時間考慮這一事情而已,就像我們不會只做模塊化開發同時不做組件化開發一樣。

1.3.模塊和子應用

模塊和子應用的區別與組件和插件的區別有點像,都在於一個統一接口。
子應用我們不常提,但其實並不少見,像微信小程序,釘釘中的第三方應用還有企業OA應用中集成的周邊功能模塊都應該屬於子應用的概念。
對於模塊而言,它暴露給外部調用的接口一般很少,最常見的就是上面提到的路由規則,相當於可以讓外部通過路由規則展示它。而子應用需要的就不只是一個展示接口,它可能需要動態的控制子應用的生命周期,以及其他功能上的信息交互(比如,賬戶信息的同步),甚至於要做到類似插件那樣的插拔效果。
所以子應用必然是接口化的,而模塊則沒有硬性的要求。

1.4.庫和框架

除了上面這四種概念,還有兩個是我們開發中常遇到的:庫和框架。
庫,或者基礎庫,概念上偏近於各種工具積累成的集合,是軟件代碼的層面是分層的概念,所以對應組件化。基礎庫甚至可以看做是一個大的組件。
而框架顧名思義是結構化的,是相對整體的一個概念,所以應用於模塊化,甚至是子應用化。
比如在iOS中,RAC是一個庫,而基於此的一套MVVM的具體實現成果(單頁面的文件結構,多頁面的交互等等)才叫框架。因為框架本身就有架構思想在里面。

2.漸進式辨析

上面講了一下幾個概念的區別,當然這幾個概念在服務端開發和客戶端開發領域可能有些微差別,我們就不深究了。想要更深入的了解這些概念的區別,我准備拿一個漸進式開發移動端項目的例子進行辨析。

首先我們定義一個虛擬的產品——一款知識類應用,包含常見的資訊、問答、學院、直播等功能。
接下來我們從設計的角度逐步拆分這個產品。

0.原始態

如果開發時沒有考慮任何組件化、模塊化開發,那么此應用的所有功能都是堆積在一起的,總結起來就是代碼特點就是高耦合,低內聚,無重用。
面對這樣的一堆代碼,技術經理可能要讓你做一下代碼重構,這就是你下一步的工作。

1.組件

那么你進行代碼重構的第一步是什么呢?
答:將工程中重復的代碼合並成為一份,也就是重用。

如果讓我們來看組件化開發的定義,它的着重點就是代碼重用。那這一步最后的結果就是提煉出一個個組件給不同的功能使用。
這里我們可以看一下其中的依賴關系:具體功能依賴提煉出來的組件,組件本身之間可能也有依賴關系,但一般不多。所以我們總結組件化開發的原則就是高重用,低耦合。當然這只是相對而言。

基於這樣的認識,我們甚至於可以把資訊、問答、學院、直播等功能封裝成組件,只不過這些組件比較大,依賴可能多些,不過本質上沒有多少區別。
就在你進行重構的過程中,這時需求來了:運營人員要求首頁頂部的九宮格樣式工具欄可動態配置,通過服務端數據修改顯示功能,並調用對應的功能頁面。

2.插件

代碼重構從來不是超然物外的,在進行過程中接到新需求也是常有的事情。那么,對於這樣一個需求,應該怎么考慮呢?
這個動態化需求很普遍,只不過這里有一個隱性要求——既然需求中要求功能動態配置,那么調用功能的地方就不知道功能的具體實現。

所以最終的方案中被調用功能必須有統一接口。我們這里說的接口只是編程領域的抽象概念,並非是指具體語言的interface或者protocol。

而有了這一統一接口,其配置功能其實就是“插拔”過程了。這樣的成果實質上已經是插件了。
插件可以解釋成可插拔式組件,它的核心就是不同功能實現提供統一接口。
項目中插件化的例子其實也不少,再舉一個例子:比如資訊和問答功能使用的彈框樣式不同,但是在兩個功能內部其彈框樣式是一致的。
面對這樣的問題,你在重構時可能會簡單的封裝出兩個組件AlertA和AlertB,分別給兩個功能使用。這樣確實很便捷,而且適合當下的場景,但是從設計或者長遠發展的角度上來考慮,如果資訊里面彈框樣式需要換成和問答一樣,甚至其他樣式,那么基於現有的方法,你就需要修改資訊功能中所有調用彈框的地方。
所以插件化是解決這個問題的好辦法:定義AlertInterface接口給具體業務功能使用,並實現AlertPluginA、AlertPluginB,在外面給不同的功能指定不能的插件即可。

3.模塊

這時候項目的組件化拆分完成,技術經理說以后不同的模塊會交由不同的人來維護,各人維護各自負責的代碼。
這個需求初看上去只是項目管理上的需求,但實際執行時若資訊、問答、學院、直播分別由四個人維護,那么他們雖然大部分代碼是相互隔離的,但仍然會有相當一部分代碼耦合在一起,有時候會同時修改同一個代碼文件。
這時候要做的自然就是模塊化。
為什么是模塊化呢?按照模塊的定義,它是以關注點進行划分的,而關注點說到底就是功能,也就是說根據我們上面的例子,資訊、問答、學院、直播可以分成不同的模塊。

我們最開始定義這個虛擬產品的時候說,它有三個特點——高耦合、低內聚、無重用。而第一點組件化開發主要是解決了重用問題,提升了部分內聚,而耦合問題則沒有涉及。
所以說我們上面可以將這個產品在邏輯上划分為資訊、問答、學院、直播四個模塊,但在代碼層面上它們卻不是四個模塊,因為它們的代碼都是混雜在一起的。比如產品首頁,可能推薦了部分資訊、顯示了熱門問答、推送了目前的直播,而這些功能的代碼則是寫在一起的;再比如程序啟動的時候,這四個模塊都需要初始化一些數據,而初始化數據的代碼也是寫在一起的。

而模塊化開發就是為了解決這一問題,即提高內聚,將分屬同一模塊代碼放到一起;降低耦合,將不同模塊間的耦合程度弱化。
高內聚是這一步的目標。但現狀是有許多地方會用到多個模塊,比如啟動的時候會調用四個模塊,首頁會展示三個模塊的界面。如果要高內聚,那么必然需要這些模塊為不同的場景提供相同的方法,這就是說所有模塊要實現同一套多個接口。
而低耦合其實並非是模塊化開發的要求,其實更多時候是基於產品上的動態化要求,所以最常見的解決方案就是路由機制。

講到這里,我們可以看到模塊化和組件化的區別就已經很明顯了。
對於一般應用而言,代碼設計優化到這一步就很好了,無論是代碼可讀性、可維護性、工作協作效率都得到了保障。不過為了講到我們上面所說的所有概念,我們不妨皮一下,給自己提點需求:
產品經理想把學院模塊單獨提出來做一個APP,但是因為技術經理反映開發資源有限,不能給這個APP配備獨立的開發人員,所以最終的結果是這個APP的功能和原應用中的學院模塊使用同一套代碼,且功能相同。

4.子應用

對於開發人員來說,這也不能算多困難的需求,只需要再創建一個新工程,將學院模塊代碼引用過來並顯示即可。
這個方案簡單直接,即便后面再把直播分離出單獨APP我也可以這樣來做。
但是,有沒有覺得這個方案和我們之前提到的Alert的例子有些神似,在這個方案中,新的工程必然直接耦合具體的模塊代碼,你需要在里面編寫很多初始化代碼。而這樣的代碼在單獨的APP中和原APP中是相當類似的。
按照《重構》一書中的提法,這是明顯的壞味道,我們應該在工程和模塊代碼之間抽象出一層接口,使兩者解除直接耦合,這樣我們甚至可以做到只需要配置就可以將一個模塊變成一個新的APP。
(或許在這個例子中,有些過度設計了,但是原諒我舉不出更合情理的例子TAT)

上面方案的成果,實際上就把學院模塊編程了學院子應用,而這個子應用被原APP和新的獨立APP所使用。
在概念上,子應用比模塊的范圍更大,子應用要求能在主應用里運行,也要求必要時可以自己運行,那么就必然要求子應用要提供生命周期接口,和主應用必要時保持一致。

其實在上面一步的模塊化開發中,有的時候也會有生命周期接口的要求,只不過並非強制,而子應用的設計中則是必須要考慮的。

3.總結

到此,我們就把組件、插件、模塊、子應用四個不同程度的設計概念異同講完了,希望讀者能有所得。文章中若有其他不足之處,懇請不吝賜教。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM