Wen Taichi

Unity Game Developer

MVP 模式的職責區分 (1) - Model | Wen Taichi

MVP 模式的職責區分 (1) - Model

March 09, 2024

※ 雖然標題是 MVP,但是概念適用於現代的 MVx 系列。

雖然網路上很容易查到 MVP、MVC、MVVM 字面上的定義,但剛開始學習的時候,還是會很容易懷疑哪些東西到底應該放在哪裡?哪些東西屬於 Model?哪些東西屬於 Presenter ?例如:Grid View 的列表資料應該要由 Presenter 還是 Model 整理出來?圖片應該要由 Presenter 先讀取,還是交給 View 自己讀取?

要能夠正確的進行職責拆分,就得理解這個模式在整個系統內的想像畫面。現代的 MVP、MVVM 及 Apple 的 MVC (嗯…其實就是 MVP) 都是以分層架構想像的,各個層級都有它們的職責。

展示層與 Model 的分離

首先從釐清 Model 開始,因為「將展示層與 Model 分離」是軟體設計的最基礎方法之一。這裡的展示層指的是 Model 以外的部分,也就是 View + Presenter。

有些新手會受到最初版本 MVC (1970 年代) 的影響 ,認為 Model 只是一些資料物件,不過最早期的這個概念是建立在當時的應用程式都還算簡單的狀況下。當規模變大時,會造成主要工作集中在 Controller,讓 Controller 變得非常臃腫,還讓 Controller 與業務邏輯纏在一起。

現代的 Model 只是底層系統的一部份,或是為了 GUI 呈現的系統轉接層。作為系統的一部份,有需要的時候當然可以負責業務邏輯,不只是單純的資料物件了。

用 Console 想像 Model 的職責

我們可以想像一下,假如底層系統邏輯不變的情況下,我們改用 Console 執行我們的程式,看起來會是什麼樣子?例如:我們要花費資源來升級我們的一個遊戲角色,我可能會在 Console 上輸入 character upgrade [id],然後程式發送了一個 HTTP Request 並收到成功回應,然後在畫面上印出 “Success!!”。

好!現在思考一下,這個想像的 Console 版本,和我們真正實作的 GUI 版本,在程式邏輯上有哪些部分應該是要相同的?

我們可以想像,發 HTTP Request 應該不用重寫,發 Request 前的本地資源檢查也不用重寫,角色在客戶端的物件資料也不用重寫,不會因為表現面只是 Console 就發生變化。

從 GUI 畫面轉換成 Console 畫面,可以重複使用的部分,差不多就是我們要的 Model 了。

我們可以想像 Console 畫面就是一種 View。透過 Presenter 解析指令後,向 Model 呼叫正確的方法,Model 就應該正常工作。Model 是「被使用」的,不需要知道外面使用它的是 Console 或是 GUI,是一個可以互動、無關畫面的獨立物件。

當然並不是說你得用 MVP 實作 Console 應用程式,只是一種思考方式,雖然有時候這樣做還不錯。

洋蔥式架構與 M-VP

如果我們用洋蔥式架構 (Onion Architecture)來思考 MVP,理論上系統與畫面無關的部分會被放在內圈,而 GUI 會在較外圈操作系統。內圈系統理論上可以獨立存在,雖然不能自己工作,但是會提供契約讓外圈知道如何使用。只要能夠滿足系統的使用契約,不管外圈是什麼東西都可以。

我們講的 MVx,就是幫內圈系統的一部份製作 UI,這一部分的內圈系統就是這個 MVx 的 Model,透過呼叫方法與事件的契約,就可以與它工作。它不用知道外面的 Presenter 長什麼樣子,它甚至可能不用知道自己是 MVP 的一部分。所以現代 MVx 系列之間的比較,其實跟 Model 一點關係都沒有,因為只是 M-VC、M-VP、M-VVM 在外圈展示層的實作差異。

而 MVP 模式 (以及其它 MVx 系列) 本身的價值,最重要的就在於「展示層與 Model 的分離」,因為實作展示層與業務邏輯時的思考面向是不一樣的。這樣一來,開發業務邏輯時,就不用太考慮展示面的問題;開發展示層的時候,就只需要考慮 UI 面的呈現。