• 使用MVP模式实现B/S和C/S平台的功能通用


    在某些项目中我们可能需要同时开发支持B/S和C/S模式的应用,在这样的背景下如何最大化的保证功能和代码通用性和适应性,就显得尤为重要了。在传统的 B/S和C/S模式下,往往都是直接在页面UI类下直接进行控件的绑定、数据的验证和对业务逻辑的访问。这样的设计缺点是:依赖UI测试困难,控件代码和业务逻辑代码交织增加维护的难度。当业务需求变更时,需要转换或支持另一种客户端时都需要重新编写大量的页面逻辑。而采用Model-View- Presenter(MVP)可以保持各层功能的独立,使页面代码更加干净,测试更方便。

           MVP简介

             MVP是从经典的MVC模式演变而来,Model实现业务逻辑,Presenter实现程序逻辑,将View和Model完全分离,View实现页面接口。如下图:

                  

    从MVP模型图上我们可以了解到:

       1 仅有Presenter能访问Model进行数据检索执行业务逻辑。

       2 View依赖Presenter,包含Presenter实体。

       3.Presenter不依赖具体的View实体,只包含View的接口(Presenter到View间的连线非直接连接)。

       4.View不能访问Model.


          为了说明使用MVP可以实现B/S和C/S模式下功能的通用,准备了一个小的实例来说明。要实现的功能就类似博客文章的分类的管理,可以浏览所有的分类添加删除分类。这个功能同时在asp.net和Windows Form中实现。

           Model (业务逻辑)

    实现的业务逻辑:查询、添加、删除分类。类图如下:

             View接口

    View接口是MVP模式中代码量最少的地方,但是如果要想快速看一个页面实现的功能就看View接口提供的对象。

    本实例中ICategoryView成员如下类图:

     1.AddedCategory:获取要输入的添加的分类;

     2.Categories :显示要所有的分类;

     3.DeletedCategory:获取输入的要删除的分类;

     4.IsFirstLoaded :获取页面是否是第一显示,如果是第一次加载的话就显示出现有的所有分类。

     5.StatusMessage :显示当前的操作处理状态。

         Presenter

    CatePresenter包括一个ICategoryView对象,类图如下:

     

    Presenter仅包括View的接口,这样就可以测试时不需依赖具体的窗口或页面。

    CategoryPresenter实现呈现层的逻辑,包括简单的输入检查,输入反馈,View控制和访问Model业务逻辑。

    CategoryPresenter的具体实现:

    public class CategoryPresenter
    {
        
    private ICategoryView _View;

        
    public CategoryPresenter(ICategoryView view)
        {
          _View 
    = view;
        }
        
    public void OnCategorySave()
        {
          
    if (string.IsNullOrEmpty(_View.AddedCategory))
          {
            
    this._View.StatusMessage = "分类名不能为空";
            
    return;
          }
          
    if (PostsLogic.ExistCategory(this._View.AddedCategory))
          {
            _View.StatusMessage 
    = "分类名已存在!";
            
    return;
          }
          PostsLogic.AddCategory(_View.AddedCategory);

          
    this._View.Categories = PostsLogic.GetCategories();
        }
        
    public void OnViewLoaded()
        {
          
    if (this._View.IsFirstLoaded)
          {
            _View.Categories 
    = PostsLogic.GetCategories();
          }
        }
        
    public void OnCategoryDelete()
        {
          
    if (string.IsNullOrEmpty(_View.DeletedCategory))
          {
            
    this._View.StatusMessage = "请选择要删除的分类名";
            
    return;
          }

          
    if (!PostsLogic.ExistCategory(_View.DeletedCategory))
          {
            
    this._View.StatusMessage = _View.DeletedCategory + ",分类名不存在!";
            
    return;
          }
          PostsLogic.RemoveCategory(_View.DeletedCategory);
          
    this._View.StatusMessage = "删除成功!";

          
    this._View.Categories = PostsLogic.GetCategories();
        }
      }

    CategoryPresenter初始化:

         Windows Form中:

    public partial class FrmCategory : Form,ICategoryView
    {
            CategoryPresenter _Presenter;
            
    public FrmCategory()
            {
                InitializeComponent();
                _Presenter 
    = new CategoryPresenter(this);
            }
    }

     .aspx页面中:

     
    public partial class _Default : System.Web.UI.Page ,ICategoryView
    {
        
    private CategoryPresenter prsenter; 
        
    protected override void OnInit(EventArgs e)
        {
          
    base.OnInit(e);
          prsenter 
    = new CategoryPresenter(this);
        }
    }

    处理显示删除添加事件:

     private void btnAdd_Click(object sender, EventArgs e)
    {
                
    this._Presenter.OnCategorySave();
    }

    private void btnDelete_Click(object sender, EventArgs e)
    {
                
    this._Presenter.OnCategoryDelete();
    }

        实现View接口

    Presenter完成后就只需将aspx页面或windows Form窗体继承并实现ICategoryView接口,同时定义CategoryPresenter实体,就可以通用数据逻辑。

    .aspx.cs页面完整代码:

    public partial class _Default : System.Web.UI.Page ,ICategoryView
    {
      
    private CategoryPresenter prsenter;
      
    private string deleteCategory;
        
    protected void Page_Load(object sender, EventArgs e)
        {
          prsenter.OnViewLoaded();
        }
        
    protected override void OnInit(EventArgs e)
        {
          
    base.OnInit(e);
          prsenter 
    = new CategoryPresenter(this);
        }

        
    #region ICategoryView 成员
        
    public string AddedCategory
        {
          
    get { return this.txtCategory.Text.Trim(); }
        }

        
    public string DeletedCategory
        {
          
    get { return this.deleteCategory; }
        }

        
    public string StatusMessage
        {
          
    set { this.lblStatusMessage.Text = value; }
        }

        
    public System.Collections.Generic.IList<string> Categories
        {
          
    set 
          {
            
    this.repCategories.DataSource = value;
            
    this.repCategories.DataBind();
          }
        }
        
    public bool IsFirstLoaded
        {
          
    get { return !this.IsPostBack; }
        }
        
    #endregion
        
    protected void repCategories_ItemCommand(object source, RepeaterCommandEventArgs e)
        {
          
    if (e.CommandName == "delete")
          {
            deleteCategory 
    = ((Label)e.Item.FindControl("lblCategory")).Text;
            prsenter.OnCategoryDelete();
          }
        }
        
    protected void btnSave_Click(object sender, EventArgs e)
        {
          prsenter.OnCategorySave();
        }
    }

     Windwos Form窗体完整代码:

     

    public partial class FrmCategory : Form,ICategoryView
        {
            CategoryPresenter _Presenter;
            
    public FrmCategory()
            {
                InitializeComponent();
                _Presenter 
    = new CategoryPresenter(this);
            }

            
    #region ICategoryView 成员

            
    public string DeletedCategory
            {
                
    get 
                {
                    
    if (this.listBox1.SelectedIndex == -1)
                    {
                        
    return string.Empty;
                    }
                    
    return this.listBox1.SelectedValue.ToString();
                }
            }
            
    public string AddedCategory
            {
                
    get { return this.txtCategory.Text; }
            }
            
    public string StatusMessage
            {
                
    set { MessageBox.Show(value); }
            }

            
    public IList<string> Categories
            {
                
    set 
                {
                    
    this.listBox1.DataSource = null;
                
    this.listBox1.DataSource = value;
                }
            }
            
    public bool IsFirstLoaded
            {
              
    get 
              {
                
    return true;
              }
            }
            
    #endregion

            
    private void btnAdd_Click(object sender, EventArgs e)
            {
                
    this._Presenter.OnCategorySave();
            }

            
    private void btnDelete_Click(object sender, EventArgs e)
            {
                
    this._Presenter.OnCategoryDelete();
            }

            
    private void FrmCategory_Load(object sender, EventArgs e)
            {
                
    this._Presenter.OnViewLoaded();
            }
    }

    页面显示如图:

    附上本实例代码  备用地址

       总结

        本文通过一个小的实例介绍MVP模式在asp.net 和windows Form中的分离通用呈现层的数据逻辑,使业务逻辑跟具体的UI窗体分离,保持了页面内容代码的干净。使用MVP模式最重要的好处是方便测试,同时有助于设计人员和编程人员分工。了解MVP的精髓,在于了解Model 、View和Preseneter之间的通信,同时结合实际情况加以选择。

  • 相关阅读:
    为什么要使用MQ消息中间件?
    趣图:这是招聘超神级别的程序员?
    基于redis的分布式锁的分析与实践
    我的java问题排查工具单
    趣图:看到这些窗户,我想插上几根网线
    SpringBoot+MyBatis+MySQL读写分离(实例)
    IntelliJ IDEA 常用快捷键
    ex3 多分类和神经网络
    9、神经网络的学习
    神经网络--反向传播详细推导过程
  • 原文地址:https://www.cnblogs.com/stalwart/p/1985670.html
Copyright © 2020-2023  润新知