白话MVP

白话MVP 前言一没有想到的是这篇文章竟然断断续续写了一个多月期间反复改了多次思想也经历了好几次升华。本来文章的题目是《MVP之七十二变》但是最终发现变来变去其实就只有两个模式MVP和MVVM而后者还是从前者中衍生的二者形差而神似正所谓——条条大路通罗马。前言二本文以及后面的几篇文章《从event折腾到command》、《AttachedBehavior技术详解》、《包式波动理念》共同构成了Prism开发的四部曲这么名字有点别扭哦。这一系列文章都是基于这两个多月来对公司的Silverlight项目进行重构时的经验之谈。一MVP之前世今生MVP模式顾名思义即Model—View—Presenter。一言以蔽之就是用Presenter控制界面View和数据Model的交互关系。通用MVP的UML图如下所示也适用于Winform和ASP.NET后者又将Presenter称为Controller接下来我要给出Winform下的MVP编程模型的模板任何地方都可以套用大致分为以下几步1ModelModel是一个只包含属性的实体类这些属性分别与View中需要绑定的控件属性相对应。比如说当前这个例子的Model只有一个属性Name绑定到View中Label的Text属性。public class PanelPresenterationModel{public string Name { get; set; }}2View在View中创建Model属性在set方法中根据传进来的Model实体将其属性分配给View中各个控件的属性而在get方法中则根据当前各个控件的属性初始化出来一个Model实体public partial class PanelView : Form{public PanelView(){InitializeComponent();}private PanelPresenterationModel model;public PanelPresenterationModel Model{get{return model as PanelPresenterationModel;}set{model value as PanelPresenterationModel;label1.Text model.Name;}}}3Presenter在Presenter中创建View属性。在构造函数中建立Model、View和Presenter的关系并初始化Model也就是的值public class PanelPresenter{public PanelPresenter(PanelView view){this.View view;//初始化Modelthis.View.Model new PanelPresenterationModel() { Name Bao, Jianqiang };}public PanelView View { get; set; }}4修改Main函数由原先直接加载View[STAThread]static void Main(){//省略一些语句Application.Run(new PanelView());}改为先创建Presenter实例然后加载Presenter的View属性[STAThread]static void Main(){//省略一些语句PanelPresenter presenter new PanelPresenter(new PanelView());Application.Run(presenter.View);}至此一个MVP模型就建立起来了效果图如下所示代码下载WindowsFormsApplication2.zip补充一点对于上述的Main函数采用的是先实例化Presenter后实例化View的方式我们称之为Presenter-first。其实还有另一种写法就是先实例化View后实例化Presenter也就是View-first。这两种方式没有优劣之分。我们将上述示例修改为View-first的方式仅供参考。代码下载WindowsFormsApplication1.zip以上代码并没有展示MVP的全部优点于是我们为这个程序添加一个按钮点击后修改窗体上显示的文本。这样把Model从View中剥离出来的好处就看到了。为此我们要在View中添加一个ButtonClick事件点击按钮就会触发这个事件public EventHandler ButtonClick;public void btnModify_Click(object sender, EventArgs e){if (ButtonClick ! null){ButtonClick(sender, e);}}相应的在Presenter的构造函数中为该事件挂上匿名方法this.View.ButtonClick delegate{this.View.Model new PanelPresenterationModel() { Name Jax.Bao };};——这样点击View中按钮的时候就会由Presenter来修改Model中的Name属性。以上这个例子只是为了说明Winform也支持MVP模式。代码下载MVPDemo1.zip但是我们看到在传统的Winform中View中的代码还是很多究其原因是缺少一种机制使得View中的控件属性和Model中的属性绑定在一起其中一个的变化会导致另一个也跟着变化。于是WPF和Silverlight应运而生它们的出现掩盖了MVP中数据绑定的复杂度将View中的代码简化到极致。我们知道在WPF和Silverlight的任何一个级别的UserControl中都拥有一个DataContext属性于是我们可以把Model绑定到这个属性上而不用在View的内部声明一个字段来保存Model属性。WPF版本的代码示例如下代码下载MVPDemo2.zip代码是不是简单了不少起码当数据改变时我们不用再关心随之带来的控件上的变化。你也许会说那个按钮的click事件放在那里看上去很碍眼额这个就不是WPF本身能解决的问题啦于是Prism应运而生。Prism版本的Demo提供如下MVPDemo3.zip我们看到在Prism中按钮的click事件被抽象为Command命令而写在xaml中同时我们在Presenter中为其附加上该命令所要执行的方法OnClick。终于View中的代码只剩下了以下几行public partial class PanelView : Window{public PanelView(){InitializeComponent();}public PanelPresenterationModel Model{get{return this.DataContext as PanelPresenterationModel;}set{this.DataContext value;}}}看到没这就是MVP模式的最高境界——View中的代码仅包括Model属性。所有使用MVP模式编程的开发者都要尽可能把Event转换为Command实现。当然有一些特殊事件是不能转换为Command的我会在下一篇文章《从event折腾到command》中进行介绍。总结一下在WPF中建立了绑定机制的的MVP架构更加丰满UML如下所示二MVVM之横空出世MVVM模式就是Model—View—ViewModel的简称是从MVP模式中衍生出来的。UML如下所示我们将上面以MVP模式编写的WPF代码修改为MVVM模式代码如下代码下载MVPDemo4.zip根据上面的代码我们发现MVVM模式在形式上具有几个特点1. View不再与Model直接绑定而是绑定ViewModelpublic partial class PanelView : Window, IPanelView{public PanelView(){InitializeComponent();}public PanelView(PanelViewModel viewModel): this(){this.Model viewModel;this.Model.View this;}public PanelViewModel Model{get{return this.DataContext as PanelViewModel;}set{this.DataContext value;}}}2. 在ViewModel中保存着对IView的引用这个IView通常是View所实现的接口。public class PanelViewModel{public IPanelView View { get; set; }}3. 在ViewModel中可以有一些属性直接与View中的元素进行绑定也可以把这些属性抽象为一个Model实体一起绑定到View上二者可以兼而有之。关于这方面的讨论请参见《Prism研究之 巧妙使用INotifyPropertyChanged》public class PanelViewModel{