1. 程式人生 > >MVC, MVP, MVVM比較以及區別

MVC, MVP, MVVM比較以及區別

MVC, MVP和MVVM都是用來解決介面呈現和邏輯程式碼分離而出現的模式。以前只是對它們有部分的瞭解,沒有深入的研究過,對於一些裡面的概念和區別也是一知半解。現在一邊查資料,並結合自己的理解,來談一下對於這三種模式思想的理解,以及它們的區別。

一,MVC, MVP, MVVM誕生的需求?

軟體中最核心的,最基本的東西是什麼? 是的,是資料。我們寫的所有程式碼,都是圍繞資料的。

圍繞著資料的產生、修改等變化,出現了業務邏輯。

圍繞著資料的顯示,出現了不同的介面技術。 

沒有很好設計的程式碼,常常就會出現資料層(持久層)和業務邏輯層還有介面程式碼耦合的情況。

ORM等框架,解耦合了業務邏輯和資料之間的耦合,業務邏輯不再關心底層資料如何儲存和讀取。所有資料呈現給業務邏輯層的就是一個個的物件。

而MVC, MVP, MMVM用來解決業務邏輯和檢視之間的耦合。

二,一段典型的耦合程式碼

{

SqlDataAdapter adapter = new SqlDataAdapter("select * from Table1","server=.;database=db;uid=sa;pwd=password");

DataSet ds = new DataSet("ds1");

adapter.Fill(ds);

this.GridView1.DataSource = ds;

this.GridView1.DataBind();

}

上面的這段程式碼中,既包含了資料訪問,還包含的頁面展示。當專案複雜程度更高,這種程式碼就會變得非常難以維護,層次也不清晰。

三,MVC模式

MVC全名是Model View Controller,是模型(model)-檢視(view)-控制器(controller)的縮寫。MVC開始是存在於桌面程式中的,M是指業務模型,V是指使用者介面,C則是控制器,使用MVC的目的是將M和V的實現程式碼分離,從而使同一個程式可以使用不同的表現形式

3.1 主動MVC

MVC的理論思想對應的是主動MVC, 這裡的主動的意思是, Model會主動通知View更新。而我們使用MVC框架, Struts, asp.net mvc等都不是主動MVC(檢視的更新都是通過Controller完成的)

Model

用於封裝與應用程式的業務邏輯相關的資料以及對資料的處理方法。

模型中資料的變化一般會通過一種重新整理機制被公佈。為了實現這種機制,那些用於監視此模型的檢視必須事先在此模型上註冊,從而,檢視可以瞭解在資料模型上發生的改變。

View

檢視層負責資料的展示。

在檢視中一般沒有程式上的邏輯。為了實現檢視上的重新整理功能,檢視需要訪問它監視的資料模型(Model),因此應該事先在被它監視的資料那裡訂閱Model的事件。

Controller

控制器是M和V之間的聯結器,用於控制應用程式的流程。它處理事件並作出響應。“事件”包括使用者的行為和資料模型上的改變。

3.2 被動MVC

下圖是被動MVC中的流程,和主動MVC不同之處是, View沒有訂閱Model資料變化的事件,等待Model來通知需要根據新的資料來更新View. 在被動MVC中,Controller負責通知View, 有資料變化,需要更新檢視。

被動MVC 中,與主動MVC的區別在於: 

1、模型對檢視和控制器一無所知,它僅僅是被它們使用 

2、控制器使用檢視,並通知它更新資料顯示 

3、檢視僅僅是在控制器通知它去模型取資料的時候它才這麼做(檢視並不會訂閱或監視模型的更新) 

3.3. Web應用中的MVC框架

Web中的MVC框架都是被動MVC模式,因為web應用中, 由於http是基於請求和響應方式協同工作的,因此當伺服器端的model(資料)發生變化時,它不會立即更新客戶端的view,只有客戶端重新請求或重新整理頁面時才更新.

下圖是典型的MVC框架中的MVC一個請求流程。

3.4 MVC總結

MVC優點

  • 由於MVC很好的分離了檢視層和業務層,所以它具有以下優點

  • 耦合性低

  • 開發速度快

  • 可維護性高

  • 沒有控制元件的概念,對html沒有封裝,易於理解

  • 和其它平臺(java, php)等更加相似。便於人才獲取

MVC使用的誤區

1.把Model理解成實體類(Entity),在MVC中Model應該包含2部分功能,一部分是處理業務邏輯,一部分是提供View顯示的資料

2.把業務邏輯全部放在Controller端這兩個誤區本質上都是對Model的作用不明導致的。Model在MVC架構中起的作用非常重要,它應該是業務邏輯真正的實現層。所以Model的實際上是Business Model(業務模型)。而Controller僅僅起一個“橋樑”作用,它負責把View的請求轉發給Model,再負責把Model處理結束的訊息通知View。Controller是用來解耦View和Model的,具體一點說,就是為了讓UI與邏輯分離(介面與程式碼分離)。

引自http://www.techopedia.com/definition/27454/model-mvc-aspnet

Techopedia explains Model (MVC)

The Model is the part of MVC which implements the domain logic. In simple terms, this logic is used to handle the data passed between the database and the user interface (UI).

The Model is known as domain object or domain entity. 

The domain objects are stored under the Models folder in ASP.NET. The domain model represents the application perspective for the data to be handled whereas a view model is required to produce the engine that generates the View.

This definition was written in the context of ASP.NET.

MVC的缺點

完美的MVC應用場景應該是這樣的:

有個Student Model, 關聯StudentListView,  StudentEditView.

對於StudentListView, Student Model提供Student的集合資料來顯示StudentListView

對於StudentEditView, Student Model提供單個Student資料來展示StudentEditView並且響應StudentEditView的儲存操作。

但是這只是完美的情況,實際應用中,在ListView上,不單單顯示Student的資訊,可能還需要這個Student的歷史成績,家庭情況,  老師資訊。而這些是Student Model不能提供的。

也許我們可以擴充套件Student Model, 將Student Model能夠提供的資訊擴充套件,包含成績資訊等,這本身也可以。但是,如果Student顯示的View,這個需要只是需要額外的成績資訊,另一個View只是需要額外的家庭資訊,Student Model是不是有些疲於奔命,你能知道還會有多少個差異化的View的需求? 而且讓邏輯端程式碼這樣不斷的修改來適應View端,好嗎?

由於MVC的設計思想是從Model出發,而沒有考慮到View端的複雜性,這樣導致的問題是Model難以符合複雜多變的View端變化。

相對這點,MVP和MVVM就要好得多。它們都獨立出了Presenter 和ViewModel來對應每個View。

四、MVP模式

MVP模式也是一種經典的介面模式。MVP中的M代表Model, V是View, P是Presenter。

下面例子中的完整程式碼:  WinformMVP原始碼(http://files.cnblogs.com/JustRun1983/WinFormMVP.zip)

大家還可以比較園中Artech的這篇文章 談談關於MVP模式中V-P互動問題(http://www.cnblogs.com/artech/archive/2010/03/25/1696205.html)

4.1 MVP的思想

MVP模式在我看來,是一個真正意義上的隔離View的細節和複雜性的模式。為什麼這麼說:

因為在其它模式中V都代表的是UI介面, 是一個html頁面,XAML檔案或者winform介面。但是在MVP模式中的V代表的是一個介面,一個將UI介面提煉而抽象出來的介面。介面意味著任何實現了該介面的介面,都能夠複用已有的Presenter和Model程式碼。

4.2 UI介面介面化

要很好的理解MVP, 就要有把UI介面介面化的能力。看下面的介面中,將紅色標記的User Control抽象一下,就能得到下面的介面

public interface IUserAdd 

       event EventHandler UserAddEvent;

       string UserName { get; set; }

       string UserAge { get; set; }

}

介面中的2個輸入框被抽象成了UserName和UserAge兩個屬性。Save按鈕的點選事件,被抽象成了事件UserAddEvent。winform中實現該介面的程式碼如下:

public partial class UserAdd : UserControl, IUserAdd 

       public event EventHandler UserAddEvent; 

       public string UserName 

       { 

           set { this.txbName.Text = value; } 

           get { return this.txbName.Text; } 

       }

       public string UserAge 

       { 

           set { this.txbAge.Text = value; } 

           get { return this.txbAge.Text; } 

       }

       public UserAdd() 

       { 

           InitializeComponent(); 

       }

       private void btnAdd_Click(object sender, EventArgs e) 

       { 

          if (UserAddEvent != null) UserAddEvent(this, e); 

       } 

   }

下面拿UserAge屬性來解釋一下,UI介面介面化的魔力。當後端程式碼要獲取介面上的年齡值,就只需要get屬性, 要更新介面顯示的時候,就只需要set屬性。

這個時候,後端程式碼對於介面的操作,被抽象成了對於UserAge屬性的操作了,也就是和具體的介面顯示無關了。

4.3 Presenter —— Model和View之間的橋樑

上文提到的後端程式碼中,包含了P和M. M和MVC中一樣,指的是邏輯程式碼。P則是Model和View之間的橋樑,負責將對應的Model和View組合到一起。

針對上面的IUserAdd, 對應的Presenter程式碼是:

public class UserAddPresenter:IPresenter 

       private readonly IUser _model; 

       private readonly IUserAdd _view; 

       private readonly ApplicationFacade _facade = ApplicationFacade.Instance; //這裡的facade是Presenter之間通訊用的,詳細可以看完整程式碼

      //Presenter建構函式中,將view和model作為引數傳入

       public UserAddPresenter(IUser model, IUserAdd view) 

       { 

           _model = model; 

           _view = view; 

           WireUpViewEvents(); 

       }

       private void WireUpViewEvents() 

       { 

           _view.UserAddEvent += _view_UserAdd; 

       }

      //當view的UserAdd事件觸發,取得UI中的資料,呼叫model邏輯處理,新增新使用者。

     //同時傳送User_ADDED訊息到系統中(系統中其它UI部分接收訊息,比如這裡的DataGrid,它接收到User_ADDED之後,會重新整理)

       private void _view_UserAdd(object sender, EventArgs e) 

       { 

           var user = new User 

                      { 

                          Name = _view.UserName, 

                          Age = Convert.ToInt32(_view.UserAge) 

                      };

           _model.AddItem(user); 

           _facade.SendNotification(ApplicationFacade.USER_ADDED); 

       }

}

4.4 MVP的程式碼結構和時序圖

這裡的MVP中的程式碼結構圖和時序圖,能夠更好的幫助理解MVP模式

4.5 MVP模式總結

在MVP裡,Presenter完全把Model和View進行了分離,主要的程式邏輯在Presenter裡實現。

而且,Presenter與具體的 View是沒有直接關聯的,而是通過定義好的介面進行互動,從而使得在變更View時候可以保持Presenter的不變,即重用! 

不僅如此,我們還可以編寫測試用的View,模擬使用者的各種操作,從而實現對Presenter的測試 —— 而不需要使用自動化的測試工具。

 我們甚至可以在Model和View都沒有完成時候,就可以通過編寫Mock Object(即實現了Model和View的介面,但沒有具體的內容的)來測試Presenter的邏輯。

MVP的優勢

1、模型與檢視完全分離,我們可以修改檢視而不影響模型 

2、可以更高效地使用模型,因為所有的互動都發生在一個地方——Presenter內部 

3、我們可以將一個Presener用於多個檢視,而不需要改變Presenter的邏輯。這個特性非常的有用,因為檢視的變化總是比模型的變化頻繁。 

4、如果我們把邏輯放在Presenter中,那麼我們就可以脫離使用者介面來測試這些邏輯(單元測試)

五, MVVM模式

5.1 MVVM模式的設計思想

MVVM模式中,一個ViewModel和一個View匹配,它沒有MVP中的IView介面,而是完全的和View繫結,所有View中的修改變化,都會自動更新到ViewModel中,同時ViewModel的任何變化也會自動同步到View上顯示。

這種自動同步之所以能夠的原因是ViewModel中的屬性都實現了observable這樣的介面,也就是說當使用屬性的set的方法,都會同時觸發屬性修改的事件,使繫結的UI自動重新整理。

(在WPF中,這個observable介面是 INotifyPropertyChanged; 在knockoutjs中,是通過函式ko.observable() 和ko.observrableCollection()來實現的)

所以MVVM比MVP更升級一步,在MVP中,V是介面IView, 解決對於介面UI的耦合; 而MVVM乾脆直接使用ViewModel和UI無縫結合, ViewModel直接就能代表UI. 但是MVVM做到這點是要依賴具體的平臺和技術實現的,比如WPF和knockoutjs, 這也就是為什麼ViewModel不需要實現介面的原因,因為對於具體平臺和技術的依賴,本質上使用MVVM模式就是不能替換UI的使用平臺的.

5.2 MVVM模式結構圖

這裡是MVVM模式的結構圖,能夠幫助更加容易的理解MVVM模式:

六, MVC, MVP和MVVM模式使用場景總結

由於在winform中無法像WPF一樣,支援資料和介面的雙向繫結以及事件的監控,所以,在winform中MVP是最佳選擇。

WPF和html介面中使用Knockout,實現了observable, 所以使用MVVM.(應該說WPF就是為使用MVVM設計的)

在web應用中,由於http是基於請求和響應方式協同工作的, 無法一直保持連線狀態,所以無法達到MVP中Presenter之間的訊息傳遞和MVVM中的ViewModel和介面之間的繫結, 所以MVC是最佳的選擇。