前言 MVC,MVP,MVVM 屬於 GUI 軟體設計,它們都強調將軟體的視圖顯示與業務邏輯進行分離,將軟體拆分為三個部分,分別負責數據操作、視圖邏輯、業務邏輯。 業務邏輯指的是任何與數據操作、視圖操作的定義與實現無關的功能。 具體來說,業務邏輯中不應該出現如何操作視圖或如何操作數據的細節。 對於這 ...
前言
MVC,MVP,MVVM 屬於 GUI 軟體設計,它們都強調將軟體的視圖顯示與業務邏輯進行分離,將軟體拆分為三個部分,分別負責數據操作、視圖邏輯、業務邏輯。
業務邏輯指的是任何與數據操作、視圖操作的定義與實現無關的功能。
具體來說,業務邏輯中不應該出現如何操作視圖或如何操作數據的細節。
對於這些操作,應該在視圖層或數據層定義相關的功能,在這些功能內實現相關操作,再由業務邏輯去調用這些功能的 API。
這三個部分對程式有一個簡單的劃分:
- Model 負責維護軟體的數據,實現那些處理數據的功能,供外部調用,它保持獨立,不依賴 View 與 Controller。
- View 負責用戶界面,將數據展示用戶,實現那些操作視圖的功能,供外部調用。
- Conttoller/Presenter/ViewModel:
負責實現軟體的業務邏輯,協調 View 與 Model,將 Model 的數據展示到 View 的用戶界面,並將 View 對數據的更改更新至 Model。
MVC
MVC 將軟體按功能進行拆分,但是對各部分的職責、各部分的協作關係沒有很明確的約束。
根據職責與關係的不同,MVC 也有不同的實現。
Passive View MVC
被動視圖指得是 View 的用戶界面被動的等待更新,它不依賴 Model 來操作數據,將 View 數據的初始化、更新以及對 Model 數據的更新交給 Controller。
這樣的定義使得 Model 與 View 都是獨立的,可被覆用。
那 Model, View, Controller 具體該承擔什麼樣的職責呢?
-
Model 的職責很明確,提供操作數據的功能。
-
Passive View 呢?是不是意味著著它只能定義如何操作視圖,但不能自行操作呢?
不然,對於那些隻影響視圖,不涉及 Model 數據操作的視圖操作,應該由 View 自行處理,沒有必要再委托 Controller 來調用。而那些與 Model 數據相關的視圖操作,可以由 View 封裝好供 Controller調用。比如獲取數據要訪問網路,屬於耗時操作,過程中要展示進度條,那針對進度條的操作就應該封裝為 API 以供外部調用 -
Controller 負責具體的業務邏輯。對於 View 中響應用戶輸入的功能,凡是涉及業務邏輯的,View 自己不實現,而是在 Controller 中實現,View 去調用。
View 可以持續響應用戶的輸入,如果將所有的業務邏輯都放在 Controller 中,會使得 Controller 任務繁重。
雖然可以通過將 Controller 拆分為多個子模塊來解決這個問題,但是思考如何為各個子模塊分配職責也是個麻煩事兒。另外, 對 Model 與 View 調用可能會分散在代碼的許多地方,難以管理。
要解決 Controller 任務重的問題,可以將部分業務邏輯轉移出去,比如 Model 與 View 之間的數據同步邏輯是可以放在 View 中。
要實現數據的同步,View 需要知道 Model 的數據變化,而 Model 因為不能依賴 View,也就不能去更新 View。要解決這個問題,可以使用觀察者模式。
Observer Pattern
引入觀察者模式,將 Model 定義為可觀察對象,這樣 View 就可以在 Model 數據更新後更新用戶界面了。除了 View, Controller 也能向 Model 註冊觀察者,執行一些與用戶界面無關的操作。
Active Model MVC / Active View MVC
View 可以主動的更新視圖,Model 也可以將自身變化主動的通知觀察者,應用觀察者模式的 MVC 可以被稱作 Active View MVC 或 Active Model MVC。
View 與 Model 之間的數據同步操作,可能是簡單的,比如將數據原封不動地傳遞,或者進行數據拼接、類型轉換;
也可能是複雜的,比如類似加解密這樣的複雜計算,訪問網路處理數據,調用第三方軟行處理數據,之後再進行同步操作。
不可能將所有的數據同步邏輯都轉移到 View 中,好的做法是將簡單的同步操作放在 View 中實現,複雜的在 Controller 中實現。
WEB MVC
在伺服器端 WEB 軟體開發也有 MVC 的概念,但是 View 往往只是一個 HTML 文檔, 不能響應用戶輸入,但瀏覽器根據 HTML 來渲染界面,倒是也符合 MVC 對 View 的定義。
MVP
MVP 就是 MVC,MVP 有兩種實現定義:
- Passive View MVP: 就是 Passive View MVC
- Supervising Controller MVP: 就是 Active View MVC,是將簡單的同步操作放在 View 中實現,複雜的在 Prennter 中實現
真要說有什麼不同,可能就是 MVP 強制約定了 View 與 Model 的關係,而 MVC 對此並無限制。
在 OOP 項目實踐中,View 被定義為介面,而 Presenter 被定義為類,View 被註入到 Presenter 中,這樣可以保證 View 與 Presenter 能被一起複用。
MVC / MVP 代碼示例
Android Java 偽代碼:
interface IView {
public void showPeogressbar();
public void cancelPeogressbar();
}
class Presenter {
private Iview view;
void Presenter(IView v){
this.view = v;
}
public void logic() {
this.view.showPeogressbar();
// dosth
this.view.cancelPeogressbar();
}
}
class MainActivity implements IView() {
private Presenter presenter;
public void onCreate() {
this.presenter = new Presenter(this);
findViewById(R.id.btn).setOnclickListente((View view)->{
this.presenter.logic();
});
}
public void showPeogressbar() {}
public void cancelPeogressbar() {}
}
MVVM
MVC 和 MVP 本質上是一樣的,而 Model-View-ViewModel(MVVM) 也是將軟體分為三個部分,ViewModel 與 MVC 的 Controller 職責也是一樣的,負責業務邏輯。MVP 並沒有給MVC引入什麼新的概念,但 MVVM 引入了新的概念:
- 將視圖從業務邏輯中分離
- 視圖狀態
- 數據驅動視圖
視圖的狀態
- 視圖中會變化的數據
- 視圖用於響應用戶輸入的業務邏輯,即視圖的行為
視圖的狀態的本質是給視圖引入了一個代理,即 ViewModel,通過代理來間接操作視圖。
分離視圖與業務邏輯
將視圖從業務邏輯中分離的核心是業務邏輯中不操作視圖,只操作數據,在業務邏輯中不會去訪問視圖的細節。分離視圖的方式是實現 數據驅動視圖。
數據驅動視圖
數據驅動視圖的直觀表現是,改變數據,視圖就更新。其原理是 ViewModel 維護一個數據集合,與 View 的狀態一一對應。本質上還是觀察者模式,View 通過觀察 ViewModel 的數據變化,來更新視圖。而 View 因用戶輸入產生的狀態變化也會主動更新到 ViewModel 的數據集合中。
需要註意的是,ViewModel 名字里的 'Model' 指得是 View 的狀態所對應的數據,而與 Model 無關,View 狀態的更新不會同步到 Model 中。
更改 ViewModel 的數據以更新 View 狀態,屬於視圖邏輯,而同步 View 狀態對應的數據到 Model中是業務邏輯,我們還要自行實現。
聲明式 UI
視圖的數據和行為的綁定操作不會憑空就能從業務邏輯中分離,我們肯定要寫代碼來實現,但是也不可能每個 View 都先編寫一個 ViewModel,那樣會有太多的樣板代碼。
既然寫代碼麻煩,那就生成代碼。使用特定的文本格式,或者說標記語言來聲明視圖,在聲明中為視圖綁定 ViewModel,為控制項綁定狀態(數據和行為),然後寫一個解析器來生成代碼。
不同的開發平臺有不同的實現,比如 Javascript Web 應用使用 HTML 或者 JSX 聲明視圖,Android 使用 XML 或 Compose 來說明視圖,而微軟的 WPF .NET 使用 xaml, Blazor 使用 razor。
使用標記語言來定義視圖,真正做到了將視圖的關註點從業務邏輯中分離,因為解析器只用編寫一次,算不得業務邏輯。
一切皆數據
數據驅動視圖的開發方式已經是所有追求先進性的 GUI 軟體開發框架所必備的了。
在 ViewModel 中是沒有 View 的細節的,只有 View 的狀態所對應的數據。開發業務邏輯的過程中可以完全當視圖不存在,一切都是數據。