本文來自 網易雲社群 。
如何設計一個通用性的子產品
前言
每個開發者都會知道,随着項目的開發,會發現業務在不斷壯大,産品線越來越豐富,而留給開發的時間卻一直有限,在有限的時間,盡快完成某個功能的疊代。是以為了減少開發成本,保證業務功能複用,我們會将一些業務獨立出來,比如直播間、消息等,做成單獨的子產品。是以想必都會都子產品化開發有所了解。
本文的目的,并不是講述如何處理子產品化後的每個子產品之間的通信問題,以及整個應用的架構問題,而是對于做了這麼多子產品後,對子產品有個總結,在需要建立一個新的子產品的時候,可以少走彎路,如何設計一個通用性的子產品。
子產品定義
獨立的業務子產品。
它群組件的差別是:
- 元件:指的是單一的功能元件,如地圖元件、支付元件、路由元件、分享元件等等。
- 子產品:指的是獨立的業務子產品,如直播間子產品、消息子產品、課程詳情子產品、課時學習子產品等等。
子產品可以依賴一些元件,子產品與子產品之間應該不要有依賴關系。
子產品類圖
在開始講述子產品的結構前,先看下完整的子產品類圖,分為内部元素,外部元素以及外部實作。 内部元素,隻能在子產品内部流動。 外部元素,在子產品内部和外部之間流通的。 外部實作,這些類需要在子產品外部定義。
領域驅動設計(DDD)的幾個基本概念
在講述子產品内的元素之間,先了解幾個DDD鐘的基本概念:
- 實體 當一個對象由其辨別(而不是屬性)區分時,這種對象稱為實體(Entity)。
- 值對象 當一個對象用于對事務進行描述而沒有唯一辨別時,它被稱作值對象(Value Object)。
- 聚合根 Aggregate(聚合)是一組相關對象的集合,作為一個整體被外界通路,聚合根(Aggregate Root)是這個聚合的根節點。
- 領域服務 一些重要的領域行為或操作,可以歸類為領域服務。它既不是實體,也不是值對象的範疇。
子產品元素
- XXXModuleClient 向外提供服務的類,提供靜态方法。
- ModuleInstance 整個子產品的最重要的類,子產品使用前需要初始化它。
- ModuleConfig 子產品功能的配置項,是否需要打開某個功能等等。
- ModuleDependency 子產品的一些依賴項,就是子產品自身無法完成的功能,或者子產品内并不關心這個功能。如對于直播間來說,它并不知道參加課程這些邏輯,這些邏輯應該是課程子產品所關心的。
- LaunchData 針對每次服務可配置的選項。
- UI視圖 如Box,它隻關心自己的行為,以及用于向頁面展示的資料。
- ActivityOfFragment 一些Box的組裝者,負責将領域模型适配成視圖模型,塞給Box。
- ILogic 所有的業務邏輯都發生在這裡,它可以調用具體的服務完成業務邏輯,也可以交給領域模型實作。
- 領域模型 針對這個子產品所抽象出來的資料模型,它不是簡單get、set,還包含了業務邏輯。ILogic中可以引用住聚合根,其他的實體不應該被引用住。這樣不對導緻對象的引用散布在各處。
- DataSource 資料來源,可以從資料庫,也可以從伺服器。最終要轉化成對應的領域模型。
- 服務對象 單個領域模型無法處理的邏輯一般會交給服務對象。
子產品初始化
最開始能想到的便是,在應用啟動的時候便對子產品進行初始化,這樣做的好處,簡單、快捷。但是增加了應用啟動耗費的時間,也增加了應用的記憶體。
更為通用常見的方式是,懶加載,在子產品用到的時候在進行初始化。
因為子產品的核心類是ModuleInstance,所有的服務都是靠它實作的。是以,子產品初始化也就是這個單例對象的初始化。這樣在需要子產品服務的時候通過擷取這個單例,如果單例未建立,則進行初始化即可。
在子產品初始化的時候,我們将初始化的流程通過一個接口,暴露給第三方使用着,使其可以參與到子產品初始化流程。
定義一個ModuleConfigAndDependency接口,提供兩個回調方法。
public interface ModuleConfigAndDependency {
//自定義修改子產品config配置
void applyConfig(Builder builder);
//自定義配置子產品依賴
void applyDependency(Builder builder);
}
在調用ModuleInstance構造器之間,先建立一個Builder,通過解析一個雙方約定好的配置檔案,擷取ModuleConfigAndDependency的實作類的名字,通過反射建立對象。将建構ModuleInstance的Builder傳遞出去,完成自定義配置。在Android中,約定的配置檔案可以寫在清單檔案中。
子產品的配置和依賴
子產品的配置分為兩種,一種是子產品初始化完成後,配置就定了。另一種,是每次啟動服務時的配置。兩種的差別在于,作用域不同。一個針對全局的,一個針對每次服務。
全局的通過子產品初始化的時候,修改Builder。
針對每次服務的,通過LaunchData配置。 在ModuleInstance中有一個存放LaunchData的Map。
依賴和配置類似,這裡就不贅述了。
子產品的領域模型
為什麼需要自己的領域模型呢?
- 既然叫做領域,那它關注的重心就是自身子產品的業務,然後我們可以将一些業務邏輯下沉到領域模型中。
- 與後端的隔離,有時候移動端與後端的開發可能是并行的,也可能移動端早于後端。那麼接口的DTO會頻繁的變動,移動端開發不得不去配合他們,影響自身的開發效率。如果移動端做好這層隔離,那麼上層業務的開發完全不會受影響,隻需要修改DataSource這層即可。
領域模型的建構來自DataSource,可以從資料庫、也可以從伺服器,最終的傳遞給上層的資料必須是領域模型。
子產品的視圖/View層
展現給使用者的是由一塊塊區域組合而成的,我們将這些區域稱之為Box,Box所關心的就隻有兩個,一個是使用者行為,一個是視圖模型。
- 對于行為,Box隻是觸發行為,比如點選,比如曝光,而具體的執行邏輯,交給外部實作。
- 對于視圖模型,Box隻是将其展現出來,如文字。
子產品的控制層/Presenter
之前View與資料之間會有耦合性,現在交給Logic來解耦。Logic可以通過定義接口的形式調用View的變化,也可以通過Message的形式通知。而View通過ILogic定義的接口來擷取需要的資料,以及邏輯。
子產品的邊界
每個子產品都有自己的上下文,因而少不了子產品與外部之間對象轉換。 是以需要定義這樣一層隔離機制,來完成子產品内的上下文與子產品外的上下文之間的流通。
可以是共享核心的形式,兩個上下文依賴部分共享的模型。這種方式,子產品可能要依賴一些通用Model子產品。 可以是防腐層的形式,一個上下文通過一些适配和轉換與另一個上下文互動。這種方式需要在子產品内定義一些簡單的值對象。
是以我們規定子產品内的類通路權限,對于使用者,它所能通路的有以下:
- 暴露給使用者的服務接口
- 用于方法調用時的依賴對象類
總結
好的子產品設計,應該是奔着可複用,高内聚,低耦合的方式。最重要的是靈活,可配置,易于擴充。
以上是最近一年開發,對于子產品設計的了解及總結。如有不足不對的地方,請多多指點~
本文已由作者陳柏甯授權網易雲社群釋出,原文連結:雲課堂Android子產品化實戰--如何設計一個通用性的子產品