前言
MVC 和 MVVM 是比較常用的前端設計模式,你能清楚地說出 MVVM 到底比 MVC 好在哪裡嗎?我最近了解了一下 SwiftUI 裡的 MVVM 是怎麼應用的,覺得應該記錄一下自己的想法,對這兩種模式的對比和優缺點做個總結。
MVC
先來回顧下 MVC 和 MVVM 各自的特徵。前端的 MVC 如下所示:
三個抽象角色,Model 代表模型,View 代表檢視,Controller 代表控制器。三個角色間的通訊如下:
- View 接收到使用者的互動請求之後,會將請求轉發給 Controller。
- Controller 解析使用者的請求之後,就會交給對應的 Model 去處理。
- Model 處理完後會通知 View。
這裡要重點關注下 MVC 中資料變化是如何響應到介面上的:Model 和 View 之間使用了觀察者模式,以便在 Model 變化時去響應介面的變化,也就是說 View 是依賴 Model 的。
MVVM
MVVM 中也有三個角色,Model 代表模型,View 代表檢視,ViewModel 代表檢視模型。通訊的步驟如下:
- View 把使用者請求傳送給 ViewModel。
- ViewModel 通知 Model 去處理。
- Model 處理完後通知 ViewModel 需要更新。
- ViewModel 通知 View 需要更新,View 從 ViewModel 中取得需要的資料。
我們應該注意到 MVVM 跟 MVC 有一些不同:View 和 Model 沒有直接的依賴關係,並且 ViewModel 會承擔一定的顯示邏輯處理。
MVVM 好在哪裡?
在 MVC 中,主要有兩個缺點:
- 當 Model 發生變化時 View 會收到通知以便取得需要的資料,這意味著 View 可以直接訪問 Model,也就是 View 耦合了 Model。那麼當 Model 發生變化時,所有相關的 View 都會受到影響。
- 因為 Model 中不包含顯示邏輯,所以不可避免地使 View 中會包含一定的業務顯示邏輯,造成 View 的職責不清和可能的程式碼重複。
以上的缺點在 MVVM 中都得到了改進。第一,View 只關聯 ViewModel,降低了耦合性;其次,View 可以關聯多個 ViewModel 來組合介面的顯示,相關聯 Model 的業務顯示邏輯程式碼可以抽離到 ViewModel 中,提高了程式碼可重用性和靈活性,並使 View 更純粹;最後,View 和 ViewModel 通常使用雙向資料繫結來簡化開發者的程式碼編寫。
MVVM 在 SwiftUI 裡怎麼用的
來看個 MVVM 的實際例子。SwiftUI 是透過繼承和屬性包裝器來實現 MVVM 的應用場景的,程式碼寫起來非常簡單:
import SwiftUI
/**
* ViewModel 層。
* ViewModel 是可觀察的物件,繼承自 ObservableObject,觀察者是 View。
*/
class ViewModel: ObservableObject {
typealias Card = MemoryGame<String>.Card
static private let emojis = ["?", "?", "?", "?", "?", "?", "✈️", "?"]
static private func createMemoryGame() -> MemoryGame<String> {
MemoryGame<String>(numberOfPairsOfCards: 4) { idx in
emojis[idx]
}
}
/**
* ViewModel 引用了 Model。
* model 屬性是 Model 層,注意有個 @Published 屬性包裝器。
* @Published 的作用是當 model 發生變化時,ViewModel 釋出通知去通知觀察者,也就是 View。
* 這裡簡化了 Model 通知 ViewModel,ViewModel 再通知 View 的過程。
*/
@Published private var model = createMemoryGame()
/**
* 以下體現的是 ViewModel 代理了對於 Model 的訪問。
* 一些顯示邏輯程式碼可以抽離到這裡。
*/
var cards: Array<Card> {
return model.cards
}
func choose(_ card: Card) {
model.choose(card)
}
}
import SwiftUI
struct EmojiMemoryGameView: View {
/**
* @ObservedObject 屬性包裝器註冊了 View 和 ViewModel 的觀察關係。
* 當 ViewModel 發生變化時 View 會重新渲染介面。
*/
@ObservedObject var vm: EmojiMemoryGame
var body: some View {
// 顯示的介面。
}
}
總結
透過以上的描述應該可以清楚地回答出 MVVM 比 MVC 到底好在哪裡了。並且可以發現不同的 UI 框架在 MVVM 的使用上可能各有側重點,比如童鞋們可以把 Vue 的 MVVM 與 SwiftUI 做個對比。