欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Android官方架構(gòu)中UseCase

 更新時(shí)間:2023年05月08日 11:53:15   作者:fundroid  
這篇文章主要為大家介紹了Android?官方架構(gòu)中的?UseCase?該怎么寫(xiě)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

1. UseCase 的用途

Android 最新的架構(gòu)規(guī)范中,引入了 Domain Layer(譯為領(lǐng)域?qū)觨r網(wǎng)域?qū)樱ㄗh大家使用 UseCase 來(lái)封裝一些復(fù)雜的業(yè)務(wù)邏輯。

傳統(tǒng)的 MVVM 架構(gòu)中,我們習(xí)慣用 ViewModel 來(lái)承載業(yè)務(wù)邏輯,隨著業(yè)務(wù)規(guī)模的擴(kuò)大,ViewModel 變得越來(lái)越肥大,職責(zé)不清。

Clean Architecture 提出的關(guān)注點(diǎn)分離和單一職責(zé)(SRP)的設(shè)計(jì)原則被廣泛認(rèn)可,因此 Android 在最新架構(gòu)中引入了 Clean Architecture 中 UseCase 的概念。ViewModel 歸屬 UI Layer,更加聚焦 UiState 的管理,UI 無(wú)關(guān)的業(yè)務(wù)邏輯下沉 UseCase,UseCase 與 ViewModel 解耦后,也可以跨 ViewModel 提供公共邏輯。

Android 架構(gòu)早期的示例代碼 todo-app 中曾經(jīng)引入過(guò) UseCase 的概念,最新架構(gòu)中只不過(guò)是將 UseCase 的思想更明確了,最新的 UseCase 示例可以從官方的 NIA 中學(xué)習(xí)。

NIA: github.com/android/now…

2. UseCase 的特點(diǎn)

官方文檔認(rèn)為 UseCase 應(yīng)該具有以下幾個(gè)特點(diǎn):

2.1 不持有狀態(tài)

可以定義自己的數(shù)據(jù)結(jié)構(gòu)類(lèi)型,但是不能持有狀態(tài)實(shí)例,像一個(gè)純函數(shù)一樣工作。甚至直接推薦大家將邏輯重寫(xiě)到 invoke 方法中,像調(diào)用函數(shù)一樣調(diào)用實(shí)例。

下面是 NIA 中的一個(gè)示例:GetRecentSearchQueriesUseCase

2.2 單一職責(zé)

嚴(yán)格遵守單一職責(zé),一個(gè) UseCase 只做一件事情,甚至其命名就是一個(gè)具體行為。掃一眼 UseCase 的文件目錄大概就知道 App 的大概功能了。

下面 NIA 中所有 UseCases:

2.3 可有可無(wú)

官方文檔中將 UseCase 定義為可選的角色,按需定義。簡(jiǎn)單的業(yè)務(wù)場(chǎng)景中允許 UI 直接訪問(wèn) Repository。如果我們將 UseCase 作為 UI 與 Data 隔離的角色,那么工程中會(huì)出現(xiàn)很多沒(méi)有太大價(jià)值的 UseCase ,可能就只有一行調(diào)用 Repoitory 的代碼。

3. 如何定義 UseCase

如上所述,官方文檔雖然對(duì) UseCase 給出了一些基本定義,但是畢竟是一個(gè)新新生概念,很多人在真正去寫(xiě)代碼的時(shí)候仍然會(huì)感覺(jué)不清晰,缺少有效指引。在究竟如何定義 UseCase 這個(gè)問(wèn)題上,還有待大家更廣泛的討論,形成可參考的共識(shí)。本文也是帶著這個(gè)目的而生,算是拋磚引玉吧。

3.1 Optional or Mandatory?

首先,官方文檔認(rèn)為 UseCase 是可選的,雖然其初衷是好的,大家都不希望出現(xiàn)太多 One-Liner 的 UseCase,但是作為一個(gè)架構(gòu)規(guī)范切忌模棱兩可,這種“可有可無(wú)”的規(guī)則其結(jié)局往往就是“無(wú)”。

業(yè)務(wù)剛起步時(shí)由于比較簡(jiǎn)單往往定義在 Repository 中,隨著業(yè)務(wù)規(guī)模的擴(kuò)大,應(yīng)該適當(dāng)?shù)迷黾?UseCase 封裝一些復(fù)雜的業(yè)務(wù)邏輯,但是實(shí)際項(xiàng)目中此時(shí)的重構(gòu)成本會(huì)讓開(kāi)發(fā)者變得“懶惰”,UseCase 最終難產(chǎn)。

那放棄 UseCase 呢?這可能會(huì)造成 Repository 的職責(zé)不清和無(wú)限膨脹,而且 Repository 往往不止有一個(gè)方法, ViewModel 直接依賴 Repository 也違反了 SOLID 中的另一個(gè)重要原則 ISP ,ViewModel 會(huì)因?yàn)椴幌嚓P(guān)的 Repository 改動(dòng)導(dǎo)致重新編譯。

ISP(Interface Segregation Principle,接口隔離原則) 要求將接口分離成更小的和更具體的接口,以便調(diào)用方只需知道其需要使用的方法。這可以提高代碼的靈活性和可重用性,并減少代碼的依賴性和耦合性。

為了降低前期判斷成本和后續(xù)重構(gòu)成本,如果我們有業(yè)務(wù)持續(xù)壯大的預(yù)期,那不妨考慮將 UseCase 作為強(qiáng)制選項(xiàng)。當(dāng)然,最好這需要研究如何降低 UseCase 帶來(lái)的模板代碼。

3.2 Class or Object?

官方建議使用 Class 定義 UseCase,每次使用都實(shí)例化一個(gè)新對(duì)象,這會(huì)做成一些重復(fù)開(kāi)銷(xiāo),那么可否用 object 定義 UseCase 呢?

UseCase 理論上可以作為單例存在,但 Class 相對(duì)于 Object 有以下兩個(gè)優(yōu)勢(shì):

  • UseCase 希望像純函數(shù)一樣工作,普通 Class 可以確保每次使用時(shí)都會(huì)創(chuàng)建一個(gè)新的實(shí)例,從而避免狀態(tài)共享和副作用等問(wèn)題。
  • 普通類(lèi)可以通過(guò)構(gòu)造參數(shù)注入不同的 Repository,UseCase 更利于復(fù)用和單元測(cè)試

如果我們強(qiáng)烈希望 UseCase 有更長(zhǎng)的生命周期,那借助 DI 框架,普通類(lèi)也可以簡(jiǎn)單的支持。例如 Dagger 中只要添加 @Singleton 注解即可

@Singleton
class GetRecentSearchQueriesUseCase @Inject constructor(
    private val recentSearchRepository: RecentSearchRepository,
) {
    operator fun invoke(limit: Int = 10): Flow<List<RecentSearchQuery>> =
        recentSearchRepository.getRecentSearchQueries(limit)
}

3.3 Class or Function?

既然我們想像函數(shù)一樣使用 UseCase ,那為什么不直接定義成 Function 呢?比如像下面這樣

fun GetRecentSearchQueriesUseCase : Flow<List<RecentSearchQuery>> 

這確實(shí)遵循了 FP 的原則,但又喪失了 OOP 封裝性的優(yōu)勢(shì):

  • UseCase 往往需要依賴 Repository 對(duì)象,一個(gè) UseCase Class 可以將 Repository 封裝為成員存儲(chǔ)。而一個(gè) UseCase Function 則需要調(diào)用方通過(guò)參數(shù)傳入,使用成本高不說(shuō),如果 UseCase 依賴的 Repository 的類(lèi)型或者數(shù)量發(fā)生變化了,調(diào)用方需要跟著修改
  • 函數(shù)起不到隔離 UI 和 Data 的作用,ViewModel 仍然需要直接依賴 Repository,為 UseCase 傳參
  • UseCase Class 可以定義一些 private 的方法,相對(duì)于 Function 更能勝任一些復(fù)雜邏輯的實(shí)現(xiàn)

可見(jiàn),在 UseCase 的定義上 Function 沒(méi)法取代 Class。當(dāng)然 Class 也帶來(lái)一些弊端:

  • 暴露多個(gè)方法,破壞 SRP 原則。所以官方推薦用 verb in present tense + noun/what (optional) + UseCase 動(dòng)詞命名,也是想讓職責(zé)更清晰。
  • 攜帶可變狀態(tài),這是大家寫(xiě) OOP 的慣性思維
  • 樣板代碼多

3.4 Function interface ?

通過(guò)前面的分析我們知道:UseCase 的定義需要兼具 FP 和 OOP 的優(yōu)勢(shì)。這讓我想到了 Function(SAM) Interface 。Function Interface 是一個(gè)單方法的接口,可以低成本創(chuàng)建一個(gè)匿名類(lèi)對(duì)象,確保對(duì)象只能有一個(gè)方法,同時(shí)具有一定封裝性,可以通過(guò)“閉包”依賴 Repository。此外,Kotlin 對(duì) SAM 提供了簡(jiǎn)化寫(xiě)法,一定程度也減少了樣板代碼。

Functional (SAM) interfaces: kotlinlang.org/docs/fun-in…

改用 Function interface 定義 GetRecentSearchQueriesUseCase 的代碼如下:

fun interface GetRecentSearchQueriesUseCase : () -> Flow<List<RecentSearchQuery>>

用它創(chuàng)建 UseCase 實(shí)例的同時(shí),實(shí)現(xiàn)函數(shù)中的邏輯

val recentSearchQueriesUseCase = GetRecentSearchQueriesUseCase {
    //...
}

我在函數(shù)實(shí)現(xiàn)中如何 Repository 呢?這要靠 DI 容器獲取。官方示例代碼中都使用 Hilt 來(lái)解耦 ViewModel 與 UseCase 的,ViewModel 不關(guān)心 UseCase 的創(chuàng)建細(xì)節(jié)。下面是 NIA 的代碼, GetRecentSearchQueriesUseCase 被自動(dòng)注入到 SearchViewModel 中。

@HiltViewModel
class SearchViewModel @Inject constructor(
    recentSearchQueriesUseCase: GetRecentSearchQueriesUseCase // UseCase 注入 VM
    //...
) : ViewModel() { 
    //...
}

Function interface 的 GetRecentSearchQueriesUseCase 沒(méi)有構(gòu)造函數(shù),需要通過(guò) Dagger 的 @Module 安裝到 DI 容器中,provideGetRecentSearchQueriesUseCase 參數(shù)中的 RecentSearchRepository 可以從容器中自動(dòng)獲取使用。

@Module
@InstallIn(ActivityComponent::class)
object UseCaseModule {
    @Provides
    fun provideGetRecentSearchQueriesUseCase(recentSearchRepository: RecentSearchRepository) =
        GetRecentSearchQueriesUseCase { limit ->
            recentSearchRepository.getRecentSearchQueries(limit)
        }
}

當(dāng)時(shí)用 Koin 作為 DI 容器時(shí)也沒(méi)問(wèn)題,代碼如下:

single<GetRecentSearchQueriesUseCase> {
    GetRecentSearchQueriesUseCase { limit ->
       recentSearchRepository.getRecentSearchQueries(limit) 
    }
}

4. 總結(jié)

UseCase 作為官方架構(gòu)中的新概念,尚沒(méi)有完全深入人心,需要不斷探索合理的使用方式,本文給出一些基本思考:

  • 考慮到架構(gòu)的擴(kuò)展性,推薦在 ViewModel 與 Repository 之間強(qiáng)制引入 UseCase,即使眼下的業(yè)務(wù)邏輯并不復(fù)雜
  • UseCase 不持有可變狀態(tài)但依賴 Repository,需要兼具 FP 與 OOP 的特性,更適合用 Class 定義而非 Function
  • 在引入 UseCase 之前應(yīng)該先引入 DI 框架,確保 ViewModel 與 UseCase 的耦合。
  • Function Interface 是 Class 之外的另一種定義 UseCase 的方式,有利于代碼更加函數(shù)式

以上就是詳解Android官方架構(gòu)中UseCase的詳細(xì)內(nèi)容,更多關(guān)于Android官方架構(gòu)UseCase 的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論