MVP模式是類似於MVC模式的一種設計模式,最近在做項目學習過程中遇到,弄了很久終於有一些眉目,這是學習過程中的一些筆記。
MVP指的是實體對象Model、視圖Viw和業務處理Presenter。MVP的作用是解耦UI渲染、業務邏輯和數據實體的關系。在普通的winform中,業務和界面是寫在一起的,一般都是同一個Load或Click方法中,使View和Controller緊密聯系。在MVP中,我們將界面渲染放在View里面,也就是winfrom的窗體應用類;將業務關系放在Presenter類中,這就是MVP中的業務數據類;最后的數據實體與數據庫的交互放在Model中,是三者各司其職。
一般的MVP中,我們是在Presenter中主動使用View,也就是界面控件形態都是由Presenter去主動控制的。而建立這兩者之間的聯系,就是在Presenter中注冊View的事件,當界面發生由用戶觸發事件時,這個事件會通過View傳遞到Presenter中,並在Presenter中調用Model的數據方法,最后Presenter調用在類中引用的View的實例去改變界面形態,下面是一些方法的實現,這里着重說明Presenter和View的關系。
首先,我們定義View的接口類IView,里面就一個數據實體的引用:

1 public interface IView<T>:IView 2 { 3 T Model { get; set; } 4 }
接下來,定義View的下一級接口,這里是定義視圖一些控件和事件:

1 public interface IMainForm<T> : IView<T> 2 { 3 Button TestButton { get;} //定義MainFrom的按鈕引用 4 TextBox TestTextBox { get; } //定義MianForm的文本框引用 5 event EventHandler ViewLoadEvent; //定義窗體加載完畢執行事件 6 event EventHandler ButtonSubmitEvent; //定義按鈕事件 7 void ShowSubmitDialog(); //定義自定義的事件 8 }
最后就是View的實現類,里面是實現的接口方法和屬性,包含一個按鈕及一個文本框,這里有一個繼承了的MvpForm類和PresenterBinding的特性,一會再說:

1 [PresenterBinding(typeof(MainFormPresenter))] 2 public partial class MainForm : MvpForm , IMainForm<MainFormModel> 3 { 4 public MainForm() 5 { 6 InitializeComponent(); 7 } 8 9 public MainFormModel Model { get; set; } 10 public TextBox TestTextBox { get { return txtText; } } 11 public Button TestButton { get { return btnSubmit; } } 12 13 public event EventHandler ViewLoadEvent; 14 public event EventHandler ButtonSubmitEvent; 15 16 private void MainForm_Load(object sender, EventArgs e) 17 { 18 if (ViewLoadEvent != null) ViewLoadEvent(sender, e); 19 } 20 21 public void ShowSubmitDialog() 22 { 23 MessageBox.Show("to submit?"); 24 } 25 26 private void btnSubmit_Click(object sender, EventArgs e) 27 { 28 if (ButtonSubmitEvent != null) ButtonSubmitEvent(sender , e); 29 } 30 }
定義完View的內容,就可以看一下Presenter,同樣,先有接口,再有實現,先定義Presenter的接口:

1 public interface IPresenter<T>:IPresenter where T : class, View.IView 2 { 3 T View { get; } 4 }
這里再定義一個Presenter的抽象類,用於統一各個不同View對應的Presenter類,其定義如下:

1 public abstract class Presenter<T> : IPresenter<T> where T : class, View.IView 2 { 3 private readonly T view; 4 5 //這里的view作為引用,用於在presenter中獲取View的實例 6 protected Presenter(T view) 7 { 8 this.view = view; 9 } 10 11 public T View { get { return view; } } 12 }
最后就是對應的View的Presenter類了:

1 class MainFormPresenter:Presenter<View.IMainForm<Model.MainFormModel>> 2 { 3 public MainFormPresenter(View.IMainForm<Model.MainFormModel> view) 4 : base(view) 5 { 6 view.Model = new Model.MainFormModel(); 7 8 view.ViewLoadEvent += On_ViewLoad; 9 view.ButtonSubmitEvent += On_ButtonSubmitClick; 10 init(); 11 } 12 13 public void init() 14 { 15 //To Do something... 16 } 17 18 public void On_ViewLoad(object sender, EventArgs e) 19 { 20 //To Do something... 21 } 22 23 public void On_ButtonSubmitClick(object sender, EventArgs e) 24 { 25 View.ShowSubmitDialog();//通過view的實例調用view的方法來改變控件形態 26 } 27 }
這里定義了Presenter和View的接口和實現,下面就是如何將這兩個不同的模塊聯合在一起,這里,使用的是.net的特性和反射。
首先,先建立特性類PresenterBindingAttribute,通過在View的實現類標記該特性類和指定對應的Presenter類,就可以獲取View和Presenter的對應關系了,該類同樣要標注特性AttributeUsage和繼承Attribute類,同時定義兩個屬性參數:

1 [AttributeUsage(AttributeTargets.Class,AllowMultiple = true)] 2 public sealed class PresenterBindingAttribute : Attribute 3 { 4 public Type PresenterType { get;private set; } 5 6 public Type ViewType { get; set; } 7 8 public PresenterBindingAttribute(Type presenterType) 9 { 10 PresenterType = presenterType; 11 ViewType = null; 12 } 13 }
接下來,就是通過反射去建立view和presenter的關系,這里建立PerformBinding類:

1 public IPresenter PerformBinding(IView viewInstance) 2 { 3 IPresenter presenter = null; 4 Type t = viewInstance.GetType(); //獲取該視圖的類類型 5 object[] attrs = t.GetCustomAttributes(typeof(PresenterBindingAttribute), false); //獲取該類上的附加特性集合 6 //遍歷特性集合,找到Presenter類型附加的特性,通過該特性建立實例 7 foreach (PresenterBindingAttribute pba in attrs) 8 { 9 Type newt = pba.PresenterType; //獲取Presenter類類型 10 //建立Presenter實例,這里的構造參數是View的對象,這樣就使兩者建立了聯系 11 Object obj = Activator.CreateInstance(pba.PresenterType, viewInstance); 12 presenter = obj as IPresenter; 13 } 14 return presenter; 15 }
那么,這個類PerformBinding在哪里使用,一般是在應用View啟動是就要注冊實例,這里為了解除類間的強耦合,就添加一個中間類。在前面的View的實現類中,是繼承自一個MvpForm的類,這個MvpForm就使注冊View和Presenter關系的類,接下來看MvpForm的實現:

1 public partial class MvpForm : Form,IView 2 { 3 private readonly PresenterBinder presenterBinder = new PresenterBinder(); 4 public MvpForm() 5 { 6 presenterBinder.PerformBinding(this); //注冊關系 7 } 8 }
這樣,就建立了View與Presenter之間的關系,每次View頁面啟動,就先執行父類MvpForm的構造函數,注冊View和Presenter的關系,相應的邏輯可以寫在Presenter中,View的作用就是作為UI的渲染。以后添加View和Presenter實現類時,只需要繼承和實現相應的類和接口,並在View實現類添加相應的對應類特性,就可實現MVP的設計關系。