# 介紹 - MVVM 和 MVI 是 Android 開發中常用的架構模式 - 許多人不清楚兩者的真正差異,導致盲從 - 影片將比較兩者的相同與差異,並提供實際範例 # 共通點 - **Model** 和 **View** 是兩者中共有的概念 - **Model** 代表商業規則與邏輯,定義資料類型與操作 - **View** 是使用者介面 (UI),負責顯示資料 - 兩者都是「展示層」的架構模式,只處理 UI 的組織,與整體的 Clean Architecture 不同 # Model (在 MVVM 與 MVI 中的角色) - Model 實現專案的商業規則,例如社交網路 app 中的使用者、評論、貼文等 - Model 通常是 Kotlin 的資料類別,儲存專案範疇內的業務邏輯 - 可選用「領域層」來進一步分離邏輯 # View (在 MVVM 與 MVI 中的角色) - View 是使用者可見的部分,對應 Android 中的 XML 佈局或 Jetpack Compose 的可組合元素 - 兩者的 View 目標一致:接收 Model 資料並顯示在 UI 上 # 差異:ViewModel 與 Intent - **MVVM**: 使用 ViewModel 管理 UI 狀態與邏輯,處理 UI 動作並更新狀態 - **MVI**: 強調「意圖 (Intent)」模式,透過封裝的行為 (Action) 來處理 UI 的互動 - 兩者在 Android 上的實現方式大致相同,但 MVI 會透過單一狀態類 (UI State) 管理所有狀態 # MVVM 範例 - ViewModel 負責管理多個 UI 狀態,如「正在加載」、「按讚狀態」等 - 每個 UI 狀態獨立更新,ViewModel 處理狀態變化 # MVI 範例 - 使用單一 UI 狀態類別 (UI State),所有影響 UI 的狀態都封裝在此 - 處理 UI 動作 (如按讚) 透過一個「意圖 (Intent)」行為來實現,並根據動作更新狀態 - MVI 中的狀態變化是替換整個狀態實例,而不是逐個字段更新 # 主要差異 - **MVVM**: 多個獨立狀態參數,ViewModel 處理單一 UI 動作 - **MVI**: 單一狀態實例 (UI State),所有狀態統一管理,透過「意圖」來處理多種行為 # 處理 UI 動作的攔截與轉發 - **UI 動作攔截**: 需要先判斷動作是否由 UI 層直接處理,例如「返回」動作應由 nav controller 處理。 - **轉發到 ViewModel**: 如果不需 UI 處理的動作,將它們轉發到 ViewModel。 # MVVM vs MVI 的選擇 - 兩者都是流行且可行的架構模式,應根據團隊需求與偏好選擇。 - 不建議強制使用某個模式,而應根據實際需求與喜好決定。 # MVI 的批評 - **額外層級**: 一些人認為 MVI 的 Intent 層 (例如 sealed interface) 是不必要的,因為可以直接在畫面中暴露回呼並呼叫 ViewModel 的函數,類似 MVVM 的做法。 - **when 表達式過大**: 批評者認為當畫面功能複雜時,MVI 中的 when 表達式可能會過大,但這可以透過私有函數解決。 - **參數過多**: 沒有使用 Intent 的情況下,可能會導致參數過多問題。 # MVVM 的批評 - **狀態合併困難**: 批評者認為 MVVM 中,若狀態為單一字段時,狀態合併較困難,特別是使用 StateFlow。 - **分離狀態的便利性**: MVVM 更易於將不同的 StateFlow 合併使用,例如驗證 email 欄位時,根據狀態變化進行反應式處理。 # MVI 的狀態變化處理 - MVI 也可以實現類似 MVVM 的狀態反應式處理,透過 `snapshotFlow` 監控狀態變化,更新狀態副本。 # 閱讀性與可維護性 - **MVI**: 較為可讀,只需檢查 UI State 類別即可了解所有狀態的變化情況。適合大型畫面。 - **MVVM**: 對於每個 StateFlow 需要兩個字段(公開的 StateFlow 和不可變的 StateFlow),大型畫面可能導致大量代碼。 # 性能考量 - MVI 中,當使用單一 UI State 並更新其中一個字段時,會觸發整個 Flow 收集器,可能造成性能瓶頸。 - **Jetpack Compose**: Compose 具備自動檢測哪些字段發生變化的能力,在某些情況下表現較好,但使用不穩定字段(如清單)時可能會出現問題。 # 結論 - **最佳實踐**: 不應過早進行性能優化,應優先考量代碼的可讀性。 - **個人偏好**: 作者偏好使用 MVI,但並不代表 MVVM 不好,兩者各有優缺點,應根據需求靈活選擇。 # 總結差異 在討論 MVVM (Model-View-ViewModel) 和 MVI (Model-View-Intent) 的差異時,這兩種架構模式主要用於分離應用程式的呈現層,但它們的實現方式有所不同。以下是主要的差異總結: ## 相似點: 1. **Model 和 View**:兩者皆有 Model(負責業務邏輯和規則)和 View(顯示給用戶的 UI)。 2. **業務邏輯位置**:Model 在這兩個模式中都承載業務邏輯,通常包含了應用的資料結構和規則,例如用戶、貼文和評論等資料類別。 3. **ViewModel 角色**:在 Android 上,無論是 MVVM 還是 MVI,ViewModel 負責管理 UI 的狀態及邏輯,並處理來自使用者的操作。 ## 主要差異: 1. **狀態管理方式**: - **MVVM**:每個 UI 狀態以單獨的屬性來管理,例如是否正在加載、貼文內容等。狀態可以是 StateFlow 或其他形式,ViewModel 將各個狀態分別管理。 - **MVI**:所有的 UI 狀態會被包裝到一個單一的狀態類別中(UI State)。每次狀態變更時,會創建一個新的狀態實例,並更新相關的欄位。 2. **使用者操作處理**: - **MVVM**:使用回調函數或直接調用 ViewModel 的方法來處理使用者操作。 - **MVI**:使用「意圖」(Intent) 來表示使用者的行為,所有操作會被封裝為一個動作 (Action),由 ViewModel 來處理。 3. **可讀性與維護性**: - **MVVM**:較多的狀態變數可能會導致 ViewModel 變得繁雜,特別是在大型專案中,每個狀態都需要單獨管理。 - **MVI**:將所有狀態合併為一個類別後,可讀性增強,但需要注意處理每次狀態改變時的性能問題,因為整個狀態實例都會更新。 4. **性能考量**: - **MVVM**:各個狀態更新時,對應的 Flow 收集器僅處理該狀態的變化,效能較高。 - **MVI**:狀態變化時,整個狀態實例都會觸發更新,可能會有性能開銷,特別是在處理較大 UI State 時。 ## 結論: MVVM 和 MVI 各有優劣,MVVM 在管理細粒度狀態和複雜反應性操作上更簡單,而 MVI 則在代碼可讀性和維護性上較優,但可能有性能問題。選擇哪種模式取決於專案的需求、團隊的熟悉度和個人偏好。 # Terminology - Intercept Actions:攔截操作,指在UI層檢查操作是否需要由UI直接處理。 - Nav Controller:導航控制器,負責處理畫面之間的導航操作。 - OnAction Lambda:處理用戶行為的匿名函數,將用戶行為傳遞給ViewModel進行處理。 - Callback:回調函數,用來處理某個操作結束後的結果。 - When Expression:Kotlin中的條件表達式,類似於switch語句,用來檢查和處理不同的情況。 - Seal Interface:密封介面,Kotlin中的特性,限制接口的實現數量。 - State Flow:Kotlin中的狀態流,用來觀察和處理狀態變更。 - Snapshot Flow:Kotlin中的特性,允許在狀態變更時觸發流。 - Compose UI:Jetpack Compose中的UI元件,用來定義用戶介面。 - State Class:狀態類別,用來描述屏幕上的所有狀態。 - Mutable State Flow:可變狀態流,允許在Kotlin中監聽和更新狀態。 - Validator:驗證器,用來檢查某些輸入或狀態是否有效。 - AsStateFlow:將Mutable State Flow轉換為不可變的State Flow。 - State Copy:Kotlin中的方法,用來創建資料類別的副本並更新其中的某些屬性。 - ViewModelScope:Kotlin中在ViewModel的範圍內執行協程的特性。 - Public State Flow:公開狀態流,允許觀察和監聽狀態變更。 - Immutable Field:不可變字段,表示無法更改的數據。 - Flow Collector:Kotlin中的數據流收集器,用來處理流中的數據變化。 - Performance Disadvantage:效能劣勢,某種設計模式或技術導致性能下降。 - Premature Optimization:過早優化,在效能問題尚未成為瓶頸時就開始進行優化。 - Readability:可讀性,代碼結構清晰易於理解的程度。 - State Class Instability:狀態類別不穩定,指Jetpack Compose無法確定某個字段是否發生變更。 - Intercept Actions:攔截操作,指在UI層檢查操作是否需要由UI直接處理。 - Nav Controller:導航控制器,負責處理畫面之間的導航操作。 - OnAction Lambda:處理用戶行為的匿名函數,將用戶行為傳遞給ViewModel進行處理。 - Callback:回調函數,用來處理某個操作結束後的結果。 - When Expression:Kotlin中的條件表達式,類似於switch語句,用來檢查和處理不同的情況。 - Seal Interface:密封介面,Kotlin中的特性,限制接口的實現數量。 - State Flow:Kotlin中的狀態流,用來觀察和處理狀態變更。 - Snapshot Flow:Kotlin中的特性,允許在狀態變更時觸發流。 - Compose UI:Jetpack Compose中的UI元件,用來定義用戶介面。 - State Class:狀態類別,用來描述屏幕上的所有狀態。 - Mutable State Flow:可變狀態流,允許在Kotlin中監聽和更新狀態。 - Validator:驗證器,用來檢查某些輸入或狀態是否有效。 - AsStateFlow:將Mutable State Flow轉換為不可變的State Flow。 - State Copy:Kotlin中的方法,用來創建資料類別的副本並更新其中的某些屬性。 - ViewModelScope:Kotlin中在ViewModel的範圍內執行協程的特性。 - Public State Flow:公開狀態流,允許觀察和監聽狀態變更。 - Immutable Field:不可變字段,表示無法更改的數據。 - Flow Collector:Kotlin中的數據流收集器,用來處理流中的數據變化。 - Performance Disadvantage:效能劣勢,某種設計模式或技術導致性能下降。 - Premature Optimization:過早優化,在效能問題尚未成為瓶頸時就開始進行優化。 - Readability:可讀性,代碼結構清晰易於理解的程度。 - State Class Instability:狀態類別不穩定,指Jetpack Compose無法確定某個字段是否發生變更。