關於代碼編寫時「抽象」概念的一點點心得

關於抽象,是面向對象的特性,之前實際寫代碼的時候,可能注意得不是很好,對於這個點的理解也不是很到位。

但是因爲公司項目涉及到了組件化,不同組件之間的通信其實是依賴抽象接口來規範的,或者說是約束的。

而一開始,我在定義抽象接口的時候,就處理得不是很好,偶爾會把具體業務層的實現帶到接口定義之中,雖然覺得這樣有點不對勁,但是卻無法參透。

後來,在 code review 的時候,我的導師針對我的問題跟我聊了聊,在受到點撥之後,我感覺我對此的認知有了些許進步。

說得簡單的,就是:

抽象層不應該對外暴露具體的實現參數,即不是面向實現編程,而要面向抽象編程。

因爲對於抽象層來說,並不應該關心具體的實現,只需要做到 “定義”,至於業務層具體怎麼實現,那是業務的事,在外人看來,只需要根據抽象層對外暴露的對應的接口方法能夠實現目的即可。

而且對於抽象層來說,要做到替換業務層的實現,對外是無感知的,但是如果抽象層參雜了業務層的東西,那還怎麼做到替換業務層的時候對抽象層不產生影響。

比如我對外定義一個抽象接口 IPageService,裏面有個抽象方法 toUserInfoPage(),用於表示跳轉到應用的用戶信息頁。

但是,在具體的實現中,假設有兩種情況,一種是用戶信息頁實際上在首頁的一個 tab 項,假設是第 X 個 tab,那跳轉到個人信息頁此時就對應跳轉到首頁第 X 個 tab 的邏輯;第二種則是用戶信息頁是一個二級頁面,那此時的邏輯則是跳轉到對應的 Activity。

此時,如果抽象層沒有定義好,針對第一種情況,則可能會將形參 Int index 也帶入到 IPageService#toUserInfoPage() 方法中,從而變成,fun toUserInfoPage(index: Int),用來在實現類 IPageImpl 中通過參數 index 來指定跳轉到首頁第 X 個 tab(即個人信息頁)。

interface IPageService {
	fun toUserInfoPage(index: Int)
}

class PageFirstImpl: IPageService {
	fun toUserInfoPage(index: Int) {
		// use index jump to user info tab
	}
}

這種情況下,其實就是對外暴露了業務層的實現,而且調用的時候必須要指定 index 爲 X 才能達到目的。

val pageService = PageFirstImpl()
// should introduce X, and should know index be X
pageService.toUserInfoPage(X)

之後,如果要該成第二種實現,此時參數 index 就相當於擺設了,不會有絲毫用處。

class PageSecondImpl: IPageService {
	fun toUserInfoPage(index: Int) {
		// start UserInfoActivity, index is useless
	}
}

而正確的實現應該是像下面那樣,在具體的實現類中直接指定 X 即可,因爲外部調用時根本是不需要知道的,只要能夠實現跳轉到用戶信息頁即可。

interface IPageService {
	fun toUserInfoPage()
}

class PageFirstImpl: IPageService {
	fun toUserInfoPage() {
		val index = X
		// jump to user info tab via local variable ‘index’
	}
}

再補充一點,即不要本末倒置地先去想到業務層應該怎麼實現,再根據實現去定義抽象層的接口方法,這個時候就會被業務層的參數給影響到。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章