作者:Soroush Khanlou,原文鏈接,原文日期:2015-12-17
譯者:zltunes;校對:Channe;定稿:shanks
我寫過許多關于讓 ViewController
變得更輕量的文章,Model-View-ViewModel
是一種常用的可以實現該目的的設計模式。我覺得 MVVM 是一種反人類的設計模式,它使架構更加混亂而非清晰。View Model
的命名很糟糕,它只是架構優化的權宜之計。對我們來說放棄這一模式反而更好。
MVVM 命名很糟糕
名稱是很重要的。一個理想的名稱能夠有效地傳達出對象是干什么的,扮演什么角色以及怎么使用它。View Model
這一名稱則沒有發揮任何作用。
在我看來,View Model
這個十分抽象的名稱實際上指的是兩個截然不同的設計模式。
View Model
的第一種含義是 model for the view
(視圖的模型)。這是一個愚蠢的對象(嚴格來講 Swift 中叫struct
),用來傳遞給某個視圖去填充它的子視圖。它不應該包含任何邏輯或方法。類似于UILabel
需要一個字符串,UIImageView
需要一張圖像,ProfileView
需要一個ProfileViewModel
。該對象直接傳遞給ProfileView
,它允許你的子視圖是私有的而非暴露給外界,這是很有用的一點。我更喜歡把這種設計模式叫做view data
,因為它把自己從view model
的另一種定義的包袱中除去了。
View model
另一種含義是介于模型和視圖控制器之間的一種模糊、抽象的對象。負責將數據傳遞給表示層,有時候也作為網絡和數據庫通道,或者表單驗證以及其他類似的任務。這種“萬金油”式的對象可以為你的控制器減壓,但往往最后會成為一個新的“下水道”,什么任務都往里面堆。
MVVM 引進太多職責
命名不夠具體,導致這個類的任務無休止地增長。到底哪些任務應該交給view model
?沒有人知道,好像什么都能交給它。
讓我們看一些例子。
-
這里將網絡請求放入
view model
,并建議你添加驗證和表示的邏輯。 -
這里只展示了如何將表示邏輯放到
view model
中,并提出為什么不把它命名為Presenter
的問題。 -
這里講到用
view model
上傳數據并綁定到ReactiveCocoa
。 -
這里用
view model
進行驗證和獲取數據。 -
這里明確建議你在
view model
中放入“其他代碼”:
view model 非常適合干這些事:驗證邏輯、用戶輸入、視圖展示邏輯、網絡請求以及其他代碼。
沒有人知道view model
到底是什么意思,所以無法就“哪些功能交給 view model”達成一致。它本身概念過于抽象。這些作者對一個 Validator
類、Presenter
類或者 Fetcher
類該做什么是沒有異議的,這些名字都起得很好,能準確傳達出對應類扮演的角色。
給用途截然不同的對象起同一個名字只能給讀者造成困惑。如果我們不能就view model
的功能達成一致,為什么要給他們起這個名字?
這種看法面臨著一個類似的挑戰,我們發現controller
這個名稱同樣太寬泛,無法包含一系列確切的任務。
你自己的類叫什么名字完全由你決定,選個好聽的就是了!
MVVM 不改變你的架構
view model
并不能從根本上改變你的應用程序的架構。這兩張圖有什么不同(原圖)?
不需要利用先進的圖片理論你也能看出這兩張圖幾乎是一樣的。
我能想到的 MVVM 模式最大的好處就是它把“下水道”從蘋果自帶的 view controoller
類轉移到了view model
這一自定義的對象。view controller
只需要專注于視圖生命周期事件即可,變得很簡潔。盡管如此,還是有“下水道”存在,僅僅是轉移了而已。
由于view model
只是給 app 添加的一層定義簡陋的模塊,仍無法解決復雜性的問題。如果你為了避免view controller
過于臃腫而創建了view model
,那么當你的代碼規模加倍的時候會發生什么?也許我們可以加一個controller-model
層。
view model
方案不能大規模擴張。對于它想解決的問題來說只能做到緩解而無法完全避免。我們需要一個更佳的方案,比如可以嘗試隨著一個對象的增大不斷去分解它,像細胞進行有絲分裂一樣。View model
只是權宜之計。
其他社區也曾面臨該問題
幾年前 Rails 社區也曾遇到這種問題,他們也想出了解決方案,我們可以從中獲得一些啟示。首先他們有臃腫的controller
以及幾乎沒什么內容的model
。這種情況下很難進行測試,所以把所有的邏輯代碼分解到model
中,最終controller
變得很簡潔,model
變得臃腫。復雜的model
依賴于ActiveRecord
和數據庫,依舊難以測試,仍需要繼續分割成更小的部分。
7 Patterns to Refactor Fat ActiveRecord Models 這篇博客受到8 Patterns to Help You Destroy Massive View Controller的影響,是這一系列想法下的產物。總而言之你仍需要將那些惱人的東西分割成小的單元,“轉移下水道”的做法僅是緩解之計。
view model
是一種完全無法應對現代編程挑戰的做法,命名糟糕,對自己包含的功能不明確,最終面臨和controller
一樣的問題。它只是對復雜情形的權宜之計,如果我們不去避免這種做法,很快又會面臨同樣的問題。
本文由 SwiftGG 翻譯組翻譯,已經獲得作者翻譯授權,最新文章請訪問 http://swift.gg。