我理解的MVC

来源:http://www.cnblogs.com/xuanbg/archive/2017/01/03/6244187.html
-Advertisement-
Play Games

前言 前一階段對MVC模式及其衍生模式做了一番比較深入的研究和實踐,這篇文章也算是一個階段性的回顧和總結。 經典MVC模式 經典MVC模式中,M是指業務模型,V是指用戶界面,C則是控制器,使用MVC的目的是將M和V的實現代碼分離,從而使同一個程式可以使用不同的表現形式。其中,View的定義比較清晰, ...


前言

前一階段對MVC模式及其衍生模式做了一番比較深入的研究和實踐,這篇文章也算是一個階段性的回顧和總結。

經典MVC模式

經典MVC模式中,M是指業務模型,V是指用戶界面,C則是控制器,使用MVC的目的是將M和V的實現代碼分離,從而使同一個程式可以使用不同的表現形式。其中,View的定義比較清晰,就是用戶界面。但對於Model和Controller的定義則較為模糊,以致在項目實踐中對它們的職責產生了很多不同的理解。其中比較主流的有下麵兩種。

1、閉環黨


比較傳統,問題是Model和Controller職責不清,在實操時容易走形

2、 開放派


MVP的前身,問題是Controller職責太重,優點是View和Model沒有了直接的關聯

對MVP的一點淺見

如果我們希望View和Model脫離關聯的話,那麼很容易就會使得所有的職能都落到Controller頭上,就如同上圖所示。這樣,Controller也就變成了Presenter,MVC也正式演化為MVP。所有的數據都由Presenter來驅動,所有的業務邏輯也由Presenter來實現。
MVP模式常見於Android,是谷歌官方推薦的App設計模式。從我找到的這張圖上,可以非常明顯地看出三者之間的關係。

Model只是一個數據通道,退化成了Repository。如果將Model擴而大之,那麼就會變成下麵這張圖描述的場景:

Model搖身一變,成了一個數據交換中心。
MVP模式的優點是實現了View和Model的解耦,缺點是Presenter職責太重。

對MVVM的一些理解

MVVM模式現在非常流行,下麵這張圖描述了MVVM模式各部分的關係和職能:

MVVM模式同樣實現了View和Model的解耦。不過和MVP模式不同的是,MVVM是從數據驅動的角度出發來解決這個問題的。ViewModel和View實現雙向數據綁定,ViewModel的數據變化自動地反應到View上。這樣,ViewModel就代表了View,成了一個Agent。ViewModel也承擔一點業務邏輯,但非常有限(也有把大量業務邏輯放在ViewModel裡面的做法,這個就和MVP沒什麼本質區別了)。所以,業務邏輯的職能就只能由Model來扛了。事實上,MVVM模式本質上是MVC模式去掉了Controller或者說是Model合併了Controller。
MVVM模式的優點是雙向數據綁定帶來的便利,Model完全不需要關心View。以數據為核心的視角非常新穎,並且在處理業務邏輯上也更加直觀。缺點其實和MVP一樣,只不過它是Model臃腫而已。

我們為什麼需要MVC?

MVC、MVP、MVVM(這裡把它們統稱為MVC),這種模式的真正好處是什麼?說實話,這個問題好思考了很久,腦海裡閃過的答案總覺得似乎還差那麼一點。最終,梳理出以下幾點,和大家探討:

專業分工的需要

程式員和設計師的技能是完全不同的,用戶界面就應該由設計師來完成而非程式員。所以,我們需要一個不包含任何邏輯代碼的View,以便由設計師來完成用戶界面的創建工作。程式員則可以專心去做和邏輯、數據相關的工作。在這個層面上,不需要考慮讓人糟心的Model/Controller還是Model/Presenter或者Model/ViewModel如何劃分職責的問題。View的分離顯然非常成功!

應對變化的需要

既然用戶界面的問題已經得到了完美解決,那麼,就該輪到業務邏輯和數據處理的問題了。需求總是在不斷變化,程式猿對於產品狗的敵意就來自於不斷地更改需求。於是,少改、好改就成了程式員的永恆目標。行之有效的套路其實也就是分層解耦而已。無論是Model/Controller還是Model/Presenter或者Model/ViewModel,不過是分層的角度和方法不同而已。

流程工藝的需要

在軟體工程領域,規範提得很多,流程提得很少,工藝幾乎沒有人提過。但在傳統的製造業和工程施工領域,最核心的就是流程和工藝。流程和工藝,是工業化生產的組織和產品品質的基礎。

在軟體工程上,代碼風格規範就是一種工藝,瀑布式開發或者敏捷開發都是流程。如何劃分Model和Controller的職責,是軟體的一個設計過程,也屬於工藝的範疇。三者之間如何依賴,其本質是一個流程問題。被依賴的一定是先於依賴者被生產出來。明確了職責劃分和依賴關係,才能科學地編製開發計劃,保證產出的代碼的品質和效率。

有『套路』可循,我們做起事情來總是會簡單快捷許多。

傳統MVC模式存在的問題

我們知道,經典MVC模式早先的主要問題是Model和Controller的職責不明,但現在,主要問題是無法進行單元測試。既然已經知道問題在哪裡,那麼,我們來想辦法解決問題就好了,但這之前,還需要解決幾個別的問題。

業務邏輯、界面邏輯和數據邏輯傻傻分不清

從廣義上講,無論是點擊按鈕打開一個對話框,還是撥動一個開關切換界面樣式,或者驗證輸入數據的合法性,都是業務邏輯,並沒有什麼必要分得那麼細。從某種意義上,把業務邏輯分為內在的、直接的反應,和需要用戶進一步操作的,狀態不確定的過程,可能更加有用。前者例如分頁顯示的列表用戶點擊了下一頁按鈕,從而產生了刷新數據的指令;後者例如用戶點擊了編輯按鈕,是否會修改數據,修改成什麼數據在這個時候都是未知的。

三者之間如何依賴

這是一個大問題!經典的VMC模式中,View是依賴於Model的。但我個人的理解是View是需求的最直接的體現,所以View應該是先驗的存在,應該是Model依賴View而不是相反。在實際的項目中,在確定原型後,設計師和負責介面的程式員會同時開始工作。同時,測試工程師也會開始編寫測試用例和單元測試代碼。他們的工作依據都是產品給出的原型。

等一個View編寫完成後,依賴於這個View的Model就可以開工了,每一個View都會對於著一個Model和若幹的介面。最後,就輪到依賴於若幹Model的Controller登場了。這樣做的好處是整個開發過程是由底向上、由表入里的,整體過程非常自然,而且結構簡潔明瞭。

如何劃分Model和Controller的職責

這是一個更大的問題,足以引起開發者的爭吵不休,就如同什麼語言最好一樣。

拋開這些分歧,我們就會看到,無論是什麼模式,它所需要解決的無非是業務邏輯和數據問題!所以,我認為問題的本質不是誰負責什麼,而是如何分離業務邏輯和數據。

特定的用戶界面需要特點的數據,有著特定的業務邏輯。這個前提之下,解耦是沒有意義的。我們要求的職責分明,無非是為了更容易應對變化。那麼,都有哪些變化呢?

  1. 界面樣式變了,但業務邏輯和數據都沒有變
  2. 界面樣式和業務邏輯變了,但數據沒有變
  3. 界面和數據變了,但業務邏輯沒有變
  4. 界面和數據沒有變,但業務邏輯變了
  5. 全都變了

界面的改變可以分為兩種,一是樣式改變,這種變化無關其他;另一種是元素變化,必然對應著數據的變化,需要修改介面。另外,就是業務邏輯的改變,而業務邏輯和數據並不存在必然關係。在MVC模式下,View的數據來源於Model,那麼,Model的職責就是負責View和介面之間的數據交互,起一個數據通道或者說是數據引擎的作用。當數據發生變化的時候,只需要修改Model即可。

既然Model承擔了數據引擎的職責,那業務邏輯就應該由Controller來承擔。同時,因為不同的View之間也會存在交互,那麼,也需要一共同的個中間人來進行轉接和調度,由於Model和View是一對一的綁定關係,並不適合承擔這個責任。所以,Controller負責業務邏輯是天然的。不過,那些和數據直接相關的事件,例如改變了一個選項後引起可用數據的變化、切換分頁載入新數據之類的和具體業務沒有關係的簡單反應型的業務邏輯,我覺得交給Model去實現更簡單和直接,並不一定要經過Controller去驅動Model。

在這種模式下,Model負責數據,Controller負責業務邏輯,整體是非常協調的。反過來看MVP和MVVM,因為迴避職責的劃分的問題導致了Presenter或Model的臃腫。

View和Model要不要雙向數據綁定

非常有必要!傳統的MVC是沒有雙向綁定的,這樣,View上面數據的變化就必須通過Controller去修改Model。而建立雙向綁定後,Controller就無需承擔這個職責了,從整體上看,職責更加分明,邏輯也會更加簡單。

改進的MVC模式

解決了以上四個問題,我們可以得到這樣的一個新的MVC模式:

這種改進模式,相對傳統的MVC模式,解決了職責不清的問題。相對於MVP和MVVM而言,沒有因迴避職責劃分問題導致的龐大而混亂的Presenter/Model。在僅僅是View樣式不同的場景下,Model是可以復用的。而使用哪個View,可以通過重載Model的構造函數來決定。事實上,即使View的元素不同造成數據不同,Model也可以利用泛型等技術手段來達到重用代碼的目的。

因為View並不依賴任何人,所以,我們可以很方便地把View替換成單元測試代碼(View本身是可根據場景需要相互替換的),只要騙過Model就OK。這個測試類一旦被Model構造出來,就會自動驗證數據、模擬用戶更新數據和發出指令。

在項目中展開的話,結構如下圖:

後記

這不是結束,而是一個開始……



您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • redis是一個key-value存儲系統。和Memcached類似,它支持存儲的value類型相對更多,包括string(字元串)、list(鏈表)、set(集合)、zset(sorted set --有序集合)和hash(哈希類型)。這些數據類型都支持push/pop、add/remove及取交... ...
  • 讓我們先來看兩個類:Base和Derived類。註意其中的whenAmISet成員變數,和方法preProcess()。 情景1:(子類無構造方法) 當.java源代碼轉換成一個.class文件後,其轉換成類似下麵的等價代碼: 輸出結果是: set when declared 情景2:(子類添加了構 ...
  • JSP 標準標簽庫(JSTL) JSP標準標簽庫(JSTL)是一個JSP標簽集合,它封裝了JSP應用的通用核心功能。 JSTL支持通用的、結構化的任務,比如迭代,條件判斷,XML文檔操作,國際化標簽,SQL標簽。 除了這些,它還提供了一個框架來使用集成JSTL的自定義標簽。 根據JSTL標簽所提供的 ...
  • 一、Map 名值對存儲的。 常用派生類HashMap類 添加: put(key,value)往集合里添加數據 刪除: clear()刪除所有 remove(key)清除單個,根據k來找 獲取: size()獲取元素的數量 get(key)根據key獲取該數據 containsKey(key)根據ke ...
  • RTX是騰訊公司推出的企業級即時通信平臺,大多數公司都在使用它,但是我們很多時候需要將自己系統或者產品的一些通知實時推送給RTX,這就需要用到RTX的服務端SDK,建議先去看看RTX的SDK開發文檔(客戶端,伺服器),我們先看看功能效果: 當然,現在很多公司都已經在RTX的基礎上升級成了企業微信,沒 ...
  • 一、 前言 本章將學習:當執行程式時,其main函數是如何被調用的,命令行參數是如何傳送給執行程式的,典型的存儲器佈局是什麼樣式,如何分配另外的存儲空間,進程如何使用環境變數,進程終止的不同方式等。另外還將說明longjmp和setjmp函數以及它們與棧的交互作用。 二、 main函數 C程式的入口 ...
  • 本文原創,轉載請註明出處! 參考文章: 《“JUC鎖”03之 公平鎖(一)》 《“JUC鎖”03之 公平鎖(二)》 AbstractOwnableSynchronizer,用於供子類存取獨占鎖的所屬線程。 #getExclusiveOwnerThread()/#setExclusiveOwnerTh ...
  • var n=0; for(var i=2;i<=1000;i++){ var zhishu=true; for(var j=2;j<i;j++){ if(i%j==0){ zhishu=false; break; } } if(zhishu==true){ document.write(i+"<br ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...