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

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

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

1. UseCase 的用途

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

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

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

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

NIA: github.com/android/now…

2. UseCase 的特點(diǎn)

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

2.1 不持有狀態(tài)

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

下面是 NIA 中的一個示例:GetRecentSearchQueriesUseCase

2.2 單一職責(zé)

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

下面 NIA 中所有 UseCases:

2.3 可有可無

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

3. 如何定義 UseCase

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

3.1 Optional or Mandatory?

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

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

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

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

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

3.2 Class or Object?

官方建議使用 Class 定義 UseCase,每次使用都實例化一個新對象,這會做成一些重復(fù)開銷,那么可否用 object 定義 UseCase 呢?

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

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

如果我們強(qiáng)烈希望 UseCase 有更長的生命周期,那借助 DI 框架,普通類也可以簡單的支持。例如 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>> 

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

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

可見,在 UseCase 的定義上 Function 沒法取代 Class。當(dāng)然 Class 也帶來一些弊端:

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

3.4 Function interface ?

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

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

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

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

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

val recentSearchQueriesUseCase = GetRecentSearchQueriesUseCase {
    //...
}

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

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

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

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

當(dāng)時用 Koin 作為 DI 容器時也沒問題,代碼如下:

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

4. 總結(jié)

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

  • 考慮到架構(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 的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Android Application級別自定義Toast

    Android Application級別自定義Toast

    這篇文章主要為大家詳細(xì)介紹了Android Application級別自定義Toast,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-08-08
  • Android開發(fā)中ProgressDialog簡單用法示例

    Android開發(fā)中ProgressDialog簡單用法示例

    這篇文章主要介紹了Android開發(fā)中ProgressDialog簡單用法,結(jié)合實例形式分析了Android使用ProgressDialog的進(jìn)度條顯示與關(guān)閉、更新等事件響應(yīng)相關(guān)操作技巧,需要的朋友可以參考下
    2017-10-10
  • Android實現(xiàn)頁面短信驗證功能

    Android實現(xiàn)頁面短信驗證功能

    這篇文章主要為大家詳細(xì)介紹了Android實現(xiàn)頁面短信驗證功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-05-05
  • Android撥打電話功能實例詳解

    Android撥打電話功能實例詳解

    這篇文章主要介紹了Android撥打電話功能,結(jié)合實例形式較為詳細(xì)的分析了Android實現(xiàn)撥打電話功能的具體步驟與功能代碼,具有一定參考借鑒價值,需要的朋友可以參考下
    2016-02-02
  • Android開發(fā)實例之多點(diǎn)觸控程序

    Android開發(fā)實例之多點(diǎn)觸控程序

    本文主要介紹 Android開發(fā)多點(diǎn)觸控,這里提供了詳細(xì)的資料和示例代碼,以及實現(xiàn)效果圖,有開發(fā)Android應(yīng)用需要這樣的功能的小伙伴可以參考下
    2016-08-08
  • Android編程實現(xiàn)google消息通知功能示例

    Android編程實現(xiàn)google消息通知功能示例

    這篇文章主要介紹了Android編程實現(xiàn)google消息通知功能,結(jié)合具體實例形式分析了Android消息處理及C#服務(wù)器端與google交互的相關(guān)操作技巧,需要的朋友可以參考下
    2017-06-06
  • Android在多種設(shè)計下實現(xiàn)懶加載機(jī)制的方法

    Android在多種設(shè)計下實現(xiàn)懶加載機(jī)制的方法

    這篇文章主要介紹了Android在多種設(shè)計下實現(xiàn)懶加載機(jī)制的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • Android性能優(yōu)化getResources()與Binder導(dǎo)致界面卡頓優(yōu)化

    Android性能優(yōu)化getResources()與Binder導(dǎo)致界面卡頓優(yōu)化

    這篇文章主要為大家介紹了Android性能優(yōu)化getResources()與Binder導(dǎo)致界面卡頓優(yōu)化示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • Android PopupWindow被輸入法彈上去之后無法恢復(fù)原位的解決辦法

    Android PopupWindow被輸入法彈上去之后無法恢復(fù)原位的解決辦法

    這篇文章主要介紹了Android PopupWindow被輸入法彈上去之后無法恢復(fù)原位的解決辦法,需要的朋友可以參考下
    2016-12-12
  • Android中ScrollView監(jiān)聽滑動距離案例講解

    Android中ScrollView監(jiān)聽滑動距離案例講解

    這篇文章主要介紹了Android中ScrollView監(jiān)聽滑動距離案例講解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08

最新評論