• 说说MVP模式


    初看MVP模式时被它复杂的包含,继承,接口搞晕,View中有Presenter,Presenter中又有View,View又要抽象出IView,View又调用Presenter的方法,Presenter又调用IView的方法.花了点时间算是搞明白了,这里说说自己的理解.

    关于MVP模式文章一搜一大把.建议先看看这些文章.

    Artceh

    http://www.cnblogs.com/artech/archive/2010/04/12/1710681.html

    http://www.cnblogs.com/artech/archive/2010/03/25/1696205.html

    Jianqiang Bao

    http://www.cnblogs.com/jax/archive/2009/10/09/1579404.html

    Microsoft

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

    理解MVP模式,其实很简单

    mvp借用 Artceh的图片

    可以把V和P的交互想象成一个HTTTP请求,View为客户端,Presenter相当于服务器端,处理流程:

    1.View发生一些事件,一般就是页面事件,按钮点击事件等.

    2.View请求Presenter处理.

    3.Presenter整合调度Model的方法处理.

    4.Presenter调用View的方法作为回应.

    流程就这么简单,搞明白这个基本就差不多了.

    开发MVP模式的一般流程

    结合实例总结一下用MVP开发的步骤.例子很简单,就是显示一个List.可以显示全部员工,也可以显示属于某个项目的员工.

    image

    1.抽象出IView.根据页面功能抽象.可分为两个部分.

    请求部分,对应Winform,Webform的各种页面,控件事件,参考Artceh的文章,请求部分应该是自定义事件.在这个例子中,没有按钮点击,也没有下拉框改变,只有页面的load事件,所以请求部分就是一个ViewLoad.

    响应部分,是提供给Presenter操作View的,例如显示数据,显示错误提示,清空控件状态等.在这个例子中当然就是把一些数组show出来,考虑到将来有可能有多个控件的数据绑定,所以把数据绑定放在一个方法中,直接this.DataBind()就可以,不用每个控件都绑定一次,简单点的话不要这个PageDataBind也可以.

    为了更清晰,我用partial 类,接口把每个关注点分开了.

    //IView
    namespace WebAppMVP.IView
    {
        /// <summary>
        /// 请求部分,对应各种事件,页面事件,按钮单击事件,下拉框改变事件等等
        /// 要在View中封装处理一下,不能直接把对应的事件丢给Presenter
        /// 所以下面的事件参数为,ListEmployeeEventArgs
        /// </summary>
        public partial interface IListEmployeeView
        {
            event EventHandler<ListEmployeeEventArgs> ViewLoad;
        }
    
        /// <summary>
        /// 回应部分
        /// </summary>
        public partial interface IListEmployeeView
        {
            void ShowEmployee(ListEmployeeViewModel vm);
            void PageDataBind();
        }
    }

    2自定义请求参数.

    请求部分是一个自定义事件,所以ListEmployeeEventArgs是这个事件的自定义参数,通过封装这些参数,就可以把与业务无关的代码过滤掉.在这个页面功能中,因为需要显示属于某个项目的员工,所以参数当然就是ProjectId(Name是多余的,我不想再查询一次,其实只需ProjectId即可).

    //EventArgs
    namespace WebAppMVP.IView.EventArgs
    {
        public class ListEmployeeEventArgs : System.EventArgs
        {
            public int? ProjectId { get; set; }
            public string Name { get; set; }
        }
    }
    3.同理,一个用作显示的参数也是必要的.
    //ViewModel
    namespace WebAppMVP.ViewModel
    {
        public class ListEmployeeViewModel
        {
            public string ProjectName { get; set; }
    
            public Employee[] Employees { get; set; }
        }
    }

    4.Presenter.

    在Presenter中用构造函数注入一个IView.

    订阅IView的事件.

    事件处理方法中包含两个部分,1根据参数处理业务,2调用IView定义的方法作为回应

    namespace WebAppMVP.Presenter
    {
        public partial class ListEmployeePresenter
        {
            IListEmployeeView m_IView { get; set; }
            MockData mockData = new MockData(); 
    
            public ListEmployeePresenter(IListEmployeeView iview)
            {
                this.m_IView = iview; 
    
                //订阅事件,即View那边发生对应的事件,这里负责处理,责任很大,包括业务逻辑(如果有另外的业务逻辑层,就服务整合和调度)
                //还要负责如何显示数据(只负责调用IView的方法,具体实现在View中),
                this.m_IView.ViewLoad += (sender, args) =>
                {
                    #region 业务逻辑
                    ListEmployeeViewModel vm = new ListEmployeeViewModel();
                    if (args.ProjectId.HasValue)
                    {
                        vm.ProjectName = args.Name;
                        vm.Employees = mockData.Employees.Where(e => e.Projects.Any(p => p.Id == args.ProjectId.Value)).Take(10).ToArray();
                    }
                    else
                    {
                        vm.ProjectName = string.Empty;
                        vm.Employees = mockData.Employees.Take(10).ToArray();
                    }
                    #endregion 
    
                    #region 显示数据,作为回应
                    this.m_IView.ShowEmployee(vm);
                    this.m_IView.PageDataBind();
                    #endregion
                };
            }
        }
    }

    5.View.

    这里我也分为三个部分.

    初始化部分在View中继承IView,实例化一个Presenter.

        /// <summary>
        /// 初始化部分,每个页面基本雷同
        /// </summary>
        public partial class ListEmployeeView : System.Web.UI.Page, IListEmployeeView
        {
            public event EventHandler<ListEmployeeEventArgs> ViewLoad;
            private ListEmployeePresenter m_Presenter;
    
            protected void Page_Init(object sender, EventArgs e)
            {
                m_Presenter = new ListEmployeePresenter(this);
            }
        }

    请求部分在对应的页面,控件事件中准备XXXEventArgs参数,触发方法IView的自定义事件.

        /// <summary>
        /// 请求部分,这里的职责就是监视页面发生什么事件,出现什么状况,准备对应的参数,请求Presenter处理它
        /// </summary>
        public partial class ListEmployeeView
        {
            protected void Page_Load(object sender, EventArgs e)
            {
                #region 准备数据,参数,UI逻辑,实现了将这些UI相关代码与真正业务逻辑隔离
                ListEmployeeEventArgs args = new ListEmployeeEventArgs();
                if (Request.QueryString.AllKeys.Contains("id"))
                {
                    var id = 0;
                    if (int.TryParse(Request.QueryString["id"], out id))
                    {
                        args.ProjectId = id;
                        args.Name = HttpUtility.UrlDecode(Request.QueryString["name"]);
                    }
                    else
                    {
                        args.ProjectId = null;
                        args.Name = string.Empty;
    
                    }
                }
                else
                {
                    args.ProjectId = null;
                    args.Name = string.Empty;
                }
                #endregion
    
                //Presenter那边已经订阅了事件,触发事件,请求Presenter处理
                ViewLoad(sender, args);
            }
        }

    响应部分,实现IView中定义的响应方法.

    namespace WebAppMVP.View
    {
    
    
        /// <summary>
        /// 响应部分,这里对应的方法的职责非常简单--显示数据,信息等等,这里不知道Presenter,没有业务逻辑.
        /// </summary>
        public partial class ListEmployeeView
        {
            public void ShowEmployee(ListEmployeeViewModel vm)
            {
                this.rpEmployee.DataSource = vm.Employees;
                this.Title = string.IsNullOrEmpty(vm.ProjectName) ? "员工列表" : vm.ProjectName + "的员工列表";
                this.lbProjectName.Text = this.Title;
            }
            public void PageDataBind()
            {
                this.DataBind();
            }
        }
    }
    就这样,基于MVP的开发就完成了.

    简单总结

    1.首先发现是,有很多XXX.cs,也就是说类文件和类,接口会多好几倍.

    2.功能,职责很清晰,可测试性也大大提高.

    3.跟传统开发差别较大,相对有点不好理解,相信这也是很少人用的原因

    4.疑问1,没有用来做过实际项目,不知道应对需求变更方面能力如何?

    5.疑问2,可以看到上面基本没有提到M,基本是PV的交互,感觉MVP中的M作用相对较弱,是一个类似Domain Service的角色,小一点的系统中M完全可以不要(不过小系统也没必要用MVP了).

    the end

  • 相关阅读:
    Codeforces 1372D Omkar and Circle
    一个估算
    CF 1348F Phoenix and Memory
    caterpillar tree
    ABC167F Bracket Sequencing
    【troubleshooting】中文输入法下,CMD 光标消失
    Visual Studio 2019创建并调试vue.js项目(Iview admin)
    Mysql知识点集合篇
    spring boot集成mybatis-plus(注解模式)
    spring boot集成mybatis(注解模式)
  • 原文地址:https://www.cnblogs.com/lemontea/p/2083564.html
Copyright © 2020-2023  润新知