MVP設計模式的實現


 

MVP:界面與業務邏輯分離在Winform中的應用

 

MVP,Model-View-Presenter的縮寫。

在MSDN上,下載了一個示例,http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/MVP.mspx?mfr=true

采用的實現結構

 

 

在Blog http://www.agilelabs.cn/blogs/wind_tower/archive/2006/01/26/626.aspx上,看到的實現結構

 

在C# 開發環境中,為了提高界面的開發效率,可采用數據綁定的形式。這樣,View中需要提供數據源,采用MSDN上定義的結構,實現起來更加方便一些。

同時,為了提高對客戶請求的響應,采用多線程。由於View中的數據綁定與Presenter中多線程的業務邏輯,需要同時訪問View中的數據源,這樣,對於一些特定的多線程處理需求,還需要Presenter提供刷新View的Public函數,並不能完全實現:Presenter業務邏輯更新后,View可以自動刷新

====================================================

MVP模式與MVC模式(轉)

MVP 是從經典的模式MVC演變而來,它們的基本思想有相通的地方:Controller/Presenter負責邏輯的處理,Model提供數據,View負責顯示。作為一種新的模式,MVP與MVC有着一個重大的區別:在MVP中View並不直接使用Model,它們之間的通信是通過Presenter (MVC中的Controller)來進行的,所有的交互都發生在Presenter內部,而在MVC中View會從直接Model中讀取數據而不是通過 Controller。Alex在他的blog中對於這兩者之間的比較很直觀也比較清楚,原文可以下面的地址找到: http://ameleta.spaces.live.com/blog/cns!5F6316345A821420!163.entry

【譯文】: Model View Presenter vs Model View Controller

簡介

在我工作中經常需要處理一些由於開發人員沒能很清楚地理解MVC和MVP模式的區別的情況下使用它們而產生的問題。在這篇文章中我將會闡述一下我對兩者之間區別的一些理解。 在N層體系結構中MVC/P模式僅僅只是用於表示層(presentation layer),理解這一點很重要。這兩個模式並不是關於怎么構建數據層(data layer)和服務層(service layer)的,而是關於怎么將數據(data)從用戶接口(view)中分離出來,以及用戶接口如何與數據進行交互的。這些模式的使用讓解除你的程序中表示層對對數據和控制邏輯的依賴,從而可以自由的變更表示層。

這兩種模式的一般性概念

1、模型(Model)表示數據模型和業務邏輯(business logic)。模型並不總是DataSet,DataTable之類的東西,它代表着一類組件(components)或類(class),這些組件或類可以向外部提供數據,同時也能從外部獲取數據並將這些數據存儲在某個地方。簡單的理解,可以把模型想象成“外觀類(facade class)”。譯注:這里的外觀是指“外觀模式”中所說的外觀。外觀的一般作用是為一個復雜的子系統提供高層次的簡單易用的訪問接口,可以參看下面的圖來理解它的原理:

 

2、視圖(View)將數據層現給用戶。一般的視圖都只是包含用戶界面(UI),而不包含界面邏輯。比如,Asp.net中包含控件的頁面(page)就是一個視圖。視圖可以從模型中讀取數據,但是不能修改或更新模型。 3、層現器(Presenter)/控制器(Controller)包含了根據用戶在視圖中的行為去更新模型的邏輯。視圖僅僅只是將用戶的行為告知控制器,而控制器負責從視圖中取得數據然后發送給模型。

MVC/P模式的核心是為了將模型從視圖/控制器中分離出來,從而使得模型獨立於它們,因此模型不包含對視圖和控制的引用。 什么是MVC(Model View Presenter)模式?

1、為了使得視圖接口可以與模型和控制器進行交互,控制器執行一些初始化事件 2、用戶通過視圖(用戶接口)執行一些操作 3、控制器處理用戶行為(可以用觀察着模式實現)並通知模型進行更新 4、模型引發一些事件,以便將改變發告知視圖 5、視圖處理模型變更的事件,然后顯示新的模型數據 6、用戶接口等待用戶的進一步操作

這一模式的有一下幾個要點: 1、視圖並不使用控制器去更新模型。控制器負責處理從視圖發送過來的用戶操作並通過與模型的交互進行數據的更新 2、控制器可以和視圖融合在一塊。Visual Studion中對Windows Forms的默認處理方式就是這樣的。【譯注:比如我們雙擊一個Button,然后在它的事件里寫處理邏輯,然后將處理的數據寫回模型中。這里處理邏輯時間應該是控制器的功能,但是我們並沒有專門寫一個控制器來做這件事情而是接受了VS的默認處理方式,將它寫在Form的代碼中,而這里的Form在MVC中它就是一個View。所以這說vs默認的處理方式是將把控制器和視圖融合在一起的。】 3、控制器不包含對視圖的渲染邏輯(rendering logic)

“主動—MVC”模式,也是通常意義下的MVC模式

 

 

【譯注:為什么說是主動的?View不是等Controller通知它Model更新了然后才從Model取數據並更新顯示,而是自己監視Model的更新(如果用觀察者模式)或主動詢問Model是否更新。前面那種等待Controller通知的方式是下面所介紹的“被動—MVC”的實現方式。】

“被動—MVC”模式 與主動MVC的區別在於: 1、模型對視圖和控制器一無所知,它僅僅是被它們使用 2、控制器使用視圖,並通知它更新數據顯示 3、視圖僅僅是在控制器通知它去模型取數據的時候它才這么做(視圖並不會訂閱或監視模型的更新) 4、控制器負責處理模型數據的變化 5、控制器可以包含對視圖的渲染邏輯

 

MVP模式

與“被動—MVC模式”很接近,區別在於“視圖並不使用模型”。在MVP模式中視圖和模型是完全分離的,他們通過Presenter進行交互。 Presenter與控制器非常相似,但是它們也有一些的區別: 1、Presenter處理視圖發送過來的用戶操作(在MVC中視圖自己處理了這些操作) 2、它用更新過的數據去更新模型(在被動MVC中控制器只是通知視圖去更新過的模型中去取新的數據,而主動MVC中模型通知視圖去更新顯示,控制器不需要做工作) 3、檢查模型的更新(與被動MVC一樣) 4、(與MVC的主要區別)從模型中取數據然后將它們發送到視圖中 5、(與MVC的主要區別)將所做的更新告知視圖 6、(與MVC的區別)用Presenter渲染視圖

 

 

MVP的優勢

1、模型與視圖完全分離,我們可以修改視圖而不影響模型 2、可以更高效地使用模型,因為所以的交互都發生在一個地方——Presenter內部 3、我們可以將一個Presener用於多個視圖,而不需要改變Presenter的邏輯。這個特性非常的有用,因為視圖的變化總是比模型的變化頻繁。 4、如果我們把邏輯放在Presenter中,那么我們就可以脫離用戶接口來測試這些邏輯(單元測試)。

MVP的問題

由於對視圖的渲染放在了Presenter中,所以視圖和Persenter的交互會過於頻繁。

還有一點你需要明白,如果Presenter過多地渲染了視圖,往往會使得它與特定的視圖的聯系過於緊密。一旦視圖需要變更,那么Presenter也需要變更了。比如說,原本用來呈現Html的Presenter現在也需要用於呈現Pdf了,那么視圖很有可能也需要變更。

附:

http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/MVP.mspx?mfr=true

http://www.codeproject.com/useritems/ModelViewPresenter.asp

============================

 

winform MVP實現

 

以前寫winform項目都是界面與實現邏輯都放在一起,自從學了mvc3之后,又看了wpf的mvvp后,覺得winform的程序可以這樣來寫。

首先定義視圖接口:

 public  interface IView<T> where T:class { T Model { get; set; } void BindingData(T model); }

然后定義Presenter接口:

復制代碼
 /// <summary> /// /// </summary> /// <typeparam name="T"></typeparam> public interface IPresention<T> where T : class { void InsertData(T model); void UpdateData(T model); void DeleteData(T model); }
復制代碼

實現IPresention<T>接口的類:

復制代碼
 public class DataDictPresentation : IPresention<ResourceDict> { IView<ResourceDict> _view; IView<IList<ResourceDict>> _viewList; public DataDictPresentation(IView<ResourceDict> view) { _view = view; if (_view.Model != null) { _view.BindingData(_view.Model); } } public DataDictPresentation(IView<IList<ResourceDict>> view) { _viewList = view; PageIndex = 1; PageSize = 14; } public int PageIndex { get; set; } public int PageCount { get; set; } public int PageSize { get; set; } public void InsertData(ResourceDict model) { } public void UpdateData(ResourceDict model) { } public void Query(ResourceDict filter) { } public void DeleteData(ResourceDict model) { } } 
復制代碼

 

最后在窗體頁面上實現:

復制代碼
 public partial class DataDictBox : Form, IView<ResourceDict> { private DataDictPresentation presentation; public DataDictBox() { InitializeComponent(); Initialize(); } private static DataDictBox _instance; public static DataDictBox Instance { get { if (_instance == null || _instance.IsDisposed) { _instance = new DataDictBox(); } return _instance; } } private void Initialize() { this.Load += delegate { presentation = new DataDictPresentation(this); }; } public ResourceDict Model { get; set; } public void BindingData(ResourceDict item) { } public void SetData(ResourceDict item) { }
復制代碼

這樣完成了一個初步的MVP結構的模型。

 

作者:大亨夢 出處:http://www.cnblogs.com/fer-team/ 業務網站:http://www.alichuntian.com 本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。
 
===============================================================

.NET程序員應該熟悉的開發模式

http://developer.51cto.com/art/201012/238088.htm

我們總會有這樣一個經驗:一個系統最不容易也最不應該變化的部分是領域邏輯,最容易變化也最應該變化的是數據的呈現方式。

在Java的各種應用中可以說是到處可見MVC,J2EE貫穿MVC的概念,android的開發方式也是類MVC的,MVC結構對於做過Java應用的人而言簡直就是司空見慣。而在.NET這邊,由於之前微軟為大家提供的各種winform、ASP.NET項目典范(比如那個petshop series)將“三層”概念很好的灌輸到了.NET程序員的大腦中,許多.NET開發者凡是做個東西都要搬出自己最拿手的IModel、IDAL這樣的神器。

其實MVC與所謂的“三層架構”是兩個層次上的東西,前者是一種結構模式,而后者則是分層的角度去說。

一件很奇怪的事情,許多人知道“三層”卻不知道MVC,其實這要歸結與.NET的早期開發技術ASP.NET和winform這些page controller的典范讓許多人對三層誇誇其談卻對MVC視而不見甚至一無所知。什么是page controller模式呢?搞.NET的大多都用過winform和webform,這種xxxform用起來很直觀,我們想要做一個程序,ok,最簡單的方式就是拖拖拽拽幾個控件,然后在一個叫code behind的東西里寫這些UI事件的處理邏輯,加一大堆變量用於記錄數據和狀態,這樣一個程序就能出爐。這種開發方式對於一些小軟件系統的開發其實效率還是蠻高的,后來人們看到其弊端---一旦修改UI,事件處理就要跟着變,但是業務還是那個業務,憑什么要修改非UI的代碼?於是有人提出“三層”,最朴素的理解就是將原本那堆事件處理里的code分成業務代碼和數據庫訪問代碼並轉移到其它類中,做多了就把那坨UI叫做UI,那坨業務代碼叫做BLL,那坨DAO叫做DAL。也就是這種架構:

image

而對於J2EE的開發者來說熟悉的是下圖。 

image

(說明:這兩幅圖copy自是daxnet文)

MVC是什么

MVC是一個很經典的結構,並且其又其思想衍生出很多變種比如MVP,MVVP。傳統的MVC結構之一是這樣的(拿主動型MVC來說):

image

比如web開發(比如ASP.NET MVC或者是Java的web開發方式),view就是純web頁面或者webservice,當提交一個表單/調用webservice或者ajax后會將數據提交給controller(當然期間可能會經過各種filterchain、listener這樣的東西)controller調用相應的業務模塊來處理這個請求,最終結果會更新View的顯示。

MVP

對於非天然MVC的框架

對於ASP.NET/winform而言,雖然可以通過改造讓其支持MVC結構的開發(比如通過定制IHttpModule、IHttpHandler雲雲),但是在企業看來這些都算是邪門武功(因為這樣會喪失xxxform在開發上的很多特性比如快速開發)。大多數使用的是mvp模式。什么是mvp呢?其實mvp是MVC的一個變種。因為用winform或者webform的話form始終是個阻礙MVC開發的問題。那么好,我們仍然使用designer和codebehind,其實一個架構設計的好壞是取決於人而不是具體的技術的,只要我們OO一時強page controller一樣好用。

image

在MVP模式中我們需要自己定制各個View(web頁面或者窗體)對應的IView和IPresenter、IModel。IView要對IPresenter暴露操作UI、數據綁定的接口,IPresenter對IView要暴露當UI事件觸發需要調用的接口,IPresenter根據IView傳遞過來的請求調用業務接口並根據結果操作UI。舉個簡單的例子,一個計算“x+y=?”的程序。如果我們這樣定義IPresenter和IView

  1. public interface IPresenter   
  2.     {   
  3.         IView View { get; set; }   
  4.         void CalculateResult();   
  5.     }  
  6.  
  7. public interface IView   
  8.     {   
  9.         IPresenter Presenter { get; set; }   
  10.         void ShowResult(string result);   
  11.         int ValueOne { get; }   
  12.         int ValueTwo { get; }   
  13.     } 

IPresenter的實現如下(這里從簡把IModel去掉了)

Presenter

  1. namespace ClientLibrary  
  2. {  
  3.     public class Presenter : IPresenter  
  4.     {  
  5.         private IView _view;  
  6.         public IView View  
  7.         {  
  8.             get 
  9.             {  
  10.                 return _view;  
  11.             }  
  12.             set 
  13.             {  
  14.                 _view = value;  
  15.                 _view.Presenter = this;  
  16.             }  
  17.         }  
  18.  
  19.         private static readonly string RESULT_FORMATTER = "{0}+{1},the result is {2}";  
  20.         public void CalculateResult()  
  21.         {  
  22.             if (_view != null)  
  23.             {  
  24. var result = string.Format(RESULT_FORMATTER, _view.ValueOne, _view.ValueTwo, _view.ValueOne + _view.ValueTwo);  
  25.                 _view.ShowResult(result);  
  26.                 this.A = 123;  
  27.             }  
  28.         }  
  29.         private int _a;  
  30.         public int A  
  31.         {  
  32.             set 
  33.             {  
  34.                 A = value;  
  35.             }  
  36.         }  
  37.     }  
View的實現如下(那silverlight為例,換成別的也行)

MainPage

  1. namespace debug  
  2. {  
  3.     public partial class MainPage : UserControl, IView  
  4.     {  
  5.         public MainPage()  
  6.         {  
  7.             InitializeComponent();  
  8.         }  
  9.  
  10.         private IPresenter _presenter;  
  11.  
  12. private void btn_Click(object sender, RoutedEventArgs e)  
  13.         {  
  14.             if (_presenter != null)  
  15.             {  
  16.                 _presenter.CalculateResult();  
  17.             }  
  18.             #region hidden  
  19.             /*int total = 0;  
  20.             try  
  21.             {  
  22. total = int.Parse(tb1.Text) + int.Parse(tb2.Text);  
  23. MessageBox.Show("計算結果:" + total.ToString());  
  24.             }  
  25.             catch (Exception ex)  
  26.             {  
  27.                 MessageBox.Show("出錯啦" + ex.ToString());  
  28.             }  
  29.             finally  
  30.             {  
  31.                 tb1.Text = string.Empty;  
  32.                 tb2.Text = string.Empty;  
  33.             }*/ 
  34.             #endregion  
  35.  
  36.         }  
  37.  
  38.         public IPresenter Presenter  
  39.         {  
  40.             get 
  41.             {  
  42.                 return _presenter;  
  43.             }  
  44.             set 
  45.             {  
  46.                 _presenter = value;  
  47.             }  
  48.         }  
  49.  
  50.         public void ShowResult(string result)  
  51.         {  
  52.             MessageBox.Show(result);  
  53.         }  
  54.  
  55.         public int ValueOne  
  56.         {  
  57.             get { return int.Parse(tb1.Text); }  
  58.         }  
  59.  
  60.         public int ValueTwo  
  61.         {  
  62.             get { return int.Parse(tb2.Text); }  
  63.         }  
  64.     }  

一個很簡單的東西,看上去寫成的要多些那么一坨東西,但是好處是顯而易見的,就是更換view非常方便,根本不用去改你的IPresenter、Presenter和業務。一切都是接口調用而不依賴具體實現,這就是好處。

你必須要懂的MVVM

對於.NET平台的開發人員,托微軟的福分我們擁有一種更為強大的模型---MVVM。這應該算是做WPF/Silverlight應用的人必懂的一種結構,WPF/silverlight天生支持數據綁定和命令綁定(不過sl在命令綁定上還比較弱),這就為我們使用MVVM創造了可能。

View是什么呢,純的View只有xaml或者附帶必要的只與View本身相關邏輯代碼。ViewModel,你可以把它理解為View具體呈現內容所依賴數據的一個抽象,在MVVM中View與ViewModel總會有一種綁定關系,一旦ViewModel中被綁定的數據發生改變View上的數據就會跟着變,相反也有可能,比如你的賬號密碼框內容發生變化,關聯的ViewModel中的數據就會被框架自動通知到。

在wpf/silverlight中,綁定是通過xaml語法來完成(雖然你可以選擇用c#來寫但不符合mvvm的宗旨),並且綁定雙方的通知機制是有框架來完成,也就是說一個會xaml和blend的美工只需事先和coder商量下“咱們的xx和xx是在哪個ViewModel上叫XXX的屬性的XXX屬性……”問題之后就可以各干各的了。那么ViewModel怎么寫,咋view中又怎么綁定到viewmodel呢?首先我們談ViewModel。

說道ViewModel你需要知道依賴屬性和依賴對象的概念,這是wpf/silverlight的基礎所以不多說。有兩種方式寫ViewModel。第一種是自己去實現INotifyPropertyChanged接口,並在屬性變化時去調用NotifyPropertyChanged事件。

為了方便我們定義一個ViewModelBase的抽象基類,然后讓其他ViewModel繼承這個基類。

ViewModelBase

  1. public abstract class ViewModelBase : System.ComponentModel.INotifyPropertyChanged, IDisposable   
  2.     {   
  3.         public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;   
  4.         protected void OnPropertyChanged(string propertyName)   
  5.         {   
  6.             if (PropertyChanged != null)   
  7.             {   
  8.       var arg = new System.ComponentModel.PropertyChangedEventArgs(propertyName);   
  9.                 PropertyChanged(this, arg);   
  10.             }   
  11.         }   
  12.         public virtual void Dispose()   
  13.         {   
  14.              
  15.         }   
  16.     } 

 

  1. DemoViewModel public class DemoViewModel : ViewModelBase     
  2.  {          
  3. #region fields          
  4. private string _propertyA;          
  5. #endregion          
  6. #region presentation properties          
  7. public string PropertyA          
  8. {              
  9. get              
  10. {                 
  11.  return _propertyA;           
  12.    }              
  13. set        
  14.       {                  
  15. if (_propertyA != value)                
  16. {                      
  17. _propertyA = value;                   
  18. base.OnPropertyChanged("PropertyA");             
  19.      }         
  20.      }        }        
  21.   #endregion    } 

第二種是利用DependencyObject和DependencyProperty。

PeopleItemViewModel

  1. public class PeopleItemViewModel : DependencyObject, IPeopleItemViewModel  
  2.     {  
  3.         public PeopleItemViewModel()  
  4.         {  
  5.               
  6.         }  
  7. public static readonly DependencyProperty SimpleUserDataProperty = DependencyProperty.Register("SimpleUserData", typeof(SimpleUserData), typeof(PeopleItemViewModel));  
  8. public static readonly DependencyProperty RelativeSimpleUserDataProperty = DependencyProperty.Register("RelativeSimpleUserData", typeof(ObservableCollection<SimpleUserData>), typeof(PeopleItemViewModel));  
  9. public static readonly DependencyProperty AllSimpleUserDataProperty = DependencyProperty.Register("AllSimpleUserData", typeof(ObservableCollection<SimpleUserData>), typeof(PeopleItemViewModel));  
  10.  
  11.         public SimpleUserData SimpleUserData  
  12.         {  
  13.             get 
  14.             {  
  15.                 return (SimpleUserData)base.GetValue(SimpleUserDataProperty);  
  16.             }  
  17.             set 
  18.             {  
  19.                 if (!base.CheckAccess())  
  20.                 {  
  21.                     Dispatcher.Invoke(new Action(  
  22.                         () =>  
  23.                         {  
  24.                             SimpleUserData = value;  
  25.                         }));  
  26.                 }  
  27.                 else 
  28.                     base.SetValue(SimpleUserDataProperty, value);  
  29.             }  
  30.         }  
  31.         public ObservableCollection<SimpleUserData> RelativeSimpleUserData  
  32.         {  
  33.             get 
  34.             {  
  35. return (ObservableCollection<SimpleUserData>)base.GetValue(RelativeSimpleUserDataProperty);  
  36.             }  
  37.             set 
  38.             {  
  39.                 if (!base.CheckAccess())  
  40.                 {  
  41.                     Dispatcher.Invoke(new Action(  
  42.                         () =>  
  43.                         {  
  44.                             RelativeSimpleUserData = value;  
  45.                         }));  
  46.                 }  
  47.                 else 
  48.                 {  
  49. base.SetValue(RelativeSimpleUserDataProperty, value);  
  50. var collectionView = CollectionViewSource.GetDefaultView(value);  
  51. collectionView.SortDescriptions.Add(new SortDescription("Distance", ListSortDirection.Ascending));  
  52.                 }  
  53.             }  
  54.         }  
  55.         public ObservableCollection<SimpleUserData> AllSimpleUserData  
  56.         {  
  57.             get 
  58.             {  
  59.       return (ObservableCollection<SimpleUserData>)base.GetValue(AllSimpleUserDataProperty);  
  60.             }  
  61.             set 
  62.             {  
  63.                 if (!base.CheckAccess())  
  64.                 {  
  65.                     Dispatcher.Invoke(new Action(  
  66.                         () =>  
  67.                         {  
  68.                             AllSimpleUserData = value;  
  69.                         }));  
  70.                 }  
  71.                 else 
  72.                 {  
  73. base.SetValue(AllSimpleUserDataProperty, value);  
  74. var collectionView = CollectionViewSource.GetDefaultView(value);  
  75. collectionView.SortDescriptions.Add(new SortDescription("Distance", ListSortDirection.Ascending));  
  76.                 }  
  77.             }  
  78.         }  

在View中綁定ViewModel。

為了方便,我們可以在app.xaml中將需要的viewmode放到全局資源字典中。

image

然后再我們的vs視圖設計器Properties(中文版顯示的是“屬性”)頁上選擇為綁定源設置綁定目標(包括source和path等)以及必要的值轉換器等等即可。

image image image

(PS:雖然vs很強大,但個人還是建議熟悉xaml的綁定語法,想當初用vs2008搞wpf的時候貌似還沒有這么方便的設計器。。。)

原文鏈接:http://www.cnblogs.com/wJiang/archive/2010/12/11/1903039.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM