iOS Design Patterns | #1 MVC, MVP, MVVM, Coordinator, VIPER
前言
身為軟體工程師一定聽過 design patterns,其中比較廣泛應用在 iOS 上的 design patterns 主要是 MVC, MVVM, MVP..等,但哪一個比較好呢?其實這題沒有標準的答案,這必須根據你目前的專案或你目前的團隊而定。
因此,最迫切的是要先了解每一個 pattern 想要解決什麼事情,pattern 彼此之間的差異是什麼,接下來會將在網路上收集的資料整理成心得筆記做分享,當然這些架構的描述有可能我的理解和你的認知不太一樣,但這都很正常,最重要的是找到最適合你的專案以及實作。
目錄
- Model-View-Controller
- MVC 的 Controller 的問題
- Model-View-Presenter
- Model-View-ViewModel
- Presenter vs ViewModel
- UINavigation 的問題
- Coordinator
- VIPER
Model-View-Controller
在 1978 年,MVC 是由 Trygve Reenskaug 提出,當時是為 Smalltalk 程式語言發明的軟體架構,目的是透過將應用程式分成三種組件:Model, View, Controller,並定義三個組件之間的交互作用,來使程式的重複利用性增加,更容易地進行程式後續的修改和擴充。
雖然 Apple 已將 developer library 標示為 retired,不過很多 iOS SDK 還是可以發現 MVC 的影子。
Model
將特定的資料封裝成資料結構和定義其資料的業務邏輯(新增/修改/更新/刪除)。
例如:
Data structure(class, struct),
Data update..等。
View
專門處理 UI 的顯示。
例如:
UIView, UIStackView, UIButton, UITextField, UILabel, UITableView..等。
Controller
作為 Model
和 View
的溝通橋樑。Controller
處理來自 View
的使用者操作,並通知 Model
進行資料更新,Controller
接收來自 Model
的更新,並將資料更新到 View
上。
例如:
View life cycle (viewDidLoad, viewWillAppear..),
Setup UI/Animation (auto layout, frame, width, height, backgroundColor..),
UIViewController,
UINavigation (push, pop, present, dismiss..),
Data conversion (parse JSON, encode, decode..),
Data source/Delegate (UITableViewDataSource, UITableViewDelegate..),
API call(Network, CoreBluetooth..),
Business logic..等。
iOS 中的 UIViewController 通常被歸類在 MVC 的 Controller。
MVC 的 Controller 的問題
依據 MVC 的職責劃分理論上很不錯且容易,但在 iOS 和 Xcode 的開發環境下不容易實現,因為 Apple 將大量的業務都放到Controller
(UIViewController) 進行,當功能越來越複雜時,Controller
就會被大量的商業邏輯塞滿,導致 Controller
過於肥大而難以維護和測試,因此 MVC 常被戲稱為 Massive-View-Controller。
Controller
是否應該依據職責再進行劃分呢?是的,既然都叫做 UIViewController,那應該將關於處理 UI 的邏輯在切分出來,不是處理 UI 的邏輯讓另一個人來負責,並由這個人來與 UIViewController 進行溝通。
所以我們將
Controller
裡不是處理 UI 邏輯的 API call, Business logic, Data conversion 移出來給這個人負責,而這個人就是Presenter
或ViewModel
。
Model-View-Presenter
MVP 是由 MVC 演變而來,主要是將 MVC 的 Controller
再進行職責劃分,解決 Controller
肥大的問題。
Model
和 MVC 的 Model
一樣。
View
專門處理 UI 的顯示,但和 MVC 的 View
些許不同,因為 UIViewController 被歸類在這。
這裡的 UIViewController 只負責處理 UI 的邏輯。
Presenter
和 MVC 的 Controller
一樣作為 Model
和 View
的溝通橋樑,但和 Controller
不同的是,Presenter
只處理不是 UI 的邏輯,例如:API, Business logic, Data conversion..。
Model-View-ViewModel
MVVM 是由 MVC 演變而來,主要是將 MVC 的 Controller
再進行職責劃分,解決 Controller
肥大的問題。
Model
和 MVC 的 Model
一樣。
View
和 MVP 的 View
一樣,UIViewController 被歸類在這。
ViewModel
和 MVC 的 Controller
一樣作為 Model
和 View
的溝通橋樑,但和 Controller
不同的是,ViewModel
只處理不是 UI 的邏輯,例如:API, Business logic, Data conversion..。
Presenter vs ViewModel
其實 Presenter
和 ViewModel
被分配的職責是一樣的,但兩者的資料傳遞方式是不一樣的。
Presenter
透過 delegate, closure block, target action 來傳遞資料。ViewModel
透過 data binding 的方式來傳遞資料,例如:Combine, RxSwift 等。
UINavigation 的問題
切換畫面的時候,可以在 UIViewController A 初始化 UIViewController B 後進行 push/present,若要從 UIViewController B 畫面切回來的話,可以 UIViewController B 在初始化 UIViewController A 後進行 pop 或 dismiss UIViewController B 自己。當然,可能也會在 completion closure 的 block 加入某些邏輯。
但仔細想想其實有點怪怪的,因為依據 SOLID 原則,UIViewController 應該只關注自己的職責,也不會知道其他 UIViewController 的存在,因此這樣的寫法會變得沒有彈性且難以維護,也無法重複使用。
若今天突然需要增加幾十個畫面在 UIViewController A 和 UIViewController B 之間,不可能要在這幾十個 UIViewController 裡進行初始化和 push/pop/present/dismiss 吧。
所以我們應該將切換畫面的職責切割出來給某一個人去做管理,這個人管理每一個 UIViewController,負責畫面的切換和順序,它就是
Coordinator
。
Coordinator
Coordinator
的職責是初始化和管理所有的 UIViewController,負責所有 UIViewController 的呈現和切換,UIViewController 彼此之間不知道彼此的存在,也不用負責切換畫面的工作。
當然 Coordinator
底下可以有子Coordinator
,例如:新會員需要有一個 Coordinator
處理註冊流程,老會員登入需要另一個Coordinator
處理登入流程。
所以若要從 UIViewController A 切換到 UIViewController B,UIViewController A 要通知 Coordinator
,由 Coordinator
進行判斷來決定下一個畫面是誰,如果是 UIViewController B 的話,就初始化並切換到該畫面。
Coordinator
的 design pattern 非常容易實作,也可以與 MVC, MVVM, MVP 共同運作,網路上常常看到的 MVVM-C 和 MVP-C 其實就是加入 Coordinator
,是非常值得學習和實作的 design pattern 之一。
若以 MVVM-C 為例,工作職責可以參考下方圖片。
VIPER
MVVM-C 和 MVP-C 看起來已經將職責劃分得更清楚了,但還有沒有其他可以再劃分出來的職責呢?當然有。
若把特定的業務邏輯和商業邏輯拉出來,也就是 API call, Business logic, Data update 拉出來讓另一個人負責,這就是常見的 Service, Manager, Helper,而在 VIPER 中就是 Interactor
。
View
和 MVVM-C 的 View
一樣。
Interactor
常見取名為 Service, Manager, Helper 的業務邏輯或商業邏輯。
Presenter
和 MVP-C 的 Presenter
類似,但不需要負責業務邏輯。Presenter
處理View
和 Interactor
之間的資料轉換,同時呼叫 Router
來幫忙處理畫面的切換。
Entity
和 MVVM-C 的 Model
一樣,就是資料結構。
Router
和 MVVM-C 的。Coordinator
一樣,負責畫面之間的切換。
總結
從以上的整理可以看出,都圍繞著 SOLID 原則,將職責劃分避免業務耦合,增加程式的可維護性和重複使用性,這些常見的 design patterns,都能運用在不同場景的 iOS App 中,沒有誰比較爛誰比較讚,一切取決於你的專案或團隊。