设计模式学习:Model View Presenter (MVP)

设计模式学习:Model View Presenter (MVP)

经常看见圆子里有人谈论 mvp mvc 的相关技术知识,一直也没怎么练习

今天正好有人管我要一个demo

需求是这样的:
利用mvp模式做个 AS.NET 和 Windows Forms 都可以运行的Demo
抽空做了一下,也不知道我个人理解的是否正确、反正demo 已经做了

以我个人的理解,MVP主要就是要实现 界面和后台业务的完全隔离。

把原来 View 要做的事情的都交给 Presenter 尽量做到最大化的分离
先看运行效果





效果差不多的;什么样式也没加丑陋了点呵呵。
业务很简单,就是加载时显示全部数据,文本框输入数据在【查询】又刷新数据

在看一下解决方案视图


13个项目,呵呵不要害怕没多少代码的;

解决方案说明:
 这里空间都省略了要不子太长了也懒得改了
 [V] 就是 View 了
     IView - 视图接口
     [WebView]
         WebView - Web下实现视图接口
         ViewSite - 网站了
     [WinView]
          WinView - Win下实现视图接口
          WinApp - WinForms 程序
 [P]
      Presenter 就一个
 [M] 基本和 PetShop 是一样的我就不多解释了
      [公用]
           ServerUtility - 里面就一个“决策类”这里和 PetShop 不一样用这个代替工厂那个层
      [数据处理实现] 实现 IDAL 的项目
            TestDAL - 测试项目,就是随机生成些数据
            WebServiceDAL - 一个使用 WebService 的 DAL
            ShareData 数据实体
      IDAL
      BILL
 [其他]
      TestWebService 一个 WebService,WebServiceDAL 用的;

在来看看项目引用的关系

上图虚线部分是,动态引用(工程里没引用),图不是什么标准图,就是描述一下各个项目之间的引用关系;
ShareData 引用的地方过多,没有画上省略了。
TestWebService 不属于模式的一部分也省略了。

代码说明部分(不重要的代码我就不讲了,有需要的在问吧)

1) View 部分

WebView、WinView 分别实现了 IView 下面那一些代码进行对比讲解
 
 先说说 OpenSource.FlashElf.MVP.IView

  IUIDefault - 程序主视图接口,继承 IView
  IView - 继承 IUIControl
  IUIControl - 留着以后扩展

  --抽象了3个控件,都继承 IUIControl
  Controls.IUIButton - 按钮控件
  Controls.IUIDataBindControl - 数据绑定控件
  Controls.IUITextControl - 文本框控件

为什么要这么封装那? 因为要做到最大化分离视图先看代码吧

【OpenSource.FlashElf.MVP.WebView.Controls.UIDataBindControl】

// [!] DataGrid 还没有实现
public   class  UIDataBindControl : IUIDataBindControl
{
 CompositeDataBoundControl _control;
 
public  UIDataBindControl(CompositeDataBoundControl control)
 {
  _control
= control;
 }
 
#region  IDataBindControl 成员

 
public   void  DataBind( object  data)
 {
  
  _control.DataSource 
=  data;
  _control.DataBind();
 }

 
#endregion
}

【OpenSource.FlashElf.MVP.WinView.Controls.UIDataBindControl】

 

public   class  UIDataBindControl : IUIDataBindControl
{
    
const   string  PropertyDataSource  =   " DataSource " ;
    Control _control;
    
    
    SetDataSource _setDataSource 
=   null ;
    
    
// DataGrid 和 DataGrid View 也没有个统一的接口,还要两个构造函数
     public  UIDataBindControl(DataGrid control)
    {
        _control 
=  control;            
        _setDataSource 
=  SetPropertyToDelegate < SetDataSource > (control, PropertyDataSource);        
        
    }
    
    
public  UIDataBindControl(DataGridView control)
    {
        _control 
=  control;            
        _setDataSource 
=  SetPropertyToDelegate < SetDataSource > (control, PropertyDataSource);
        
    }
    
    
    
#region  IDataBindControl 成员

    
public   void  DataBind( object  data)
    {
        
        _setDataSource(data);
        
    }
    
    
#endregion


    
///   <summary>
    
///  [!]暂时放在这里
    
///  通用函数,把set属性转换为委托,以提高性能
    
///  没办法 DataGrid,DataGridView 也没有个通用的接口
    
///   </summary>
    
///   <typeparam name="T"></typeparam>
    
///   <param name="control"></param>
    
///   <param name="prname"></param>
    
///   <returns></returns>
     private   static  T SetPropertyToDelegate < T > (Control control,  string  prname)  where  T :  class
    {
        Type t 
=  control.GetType();

        PropertyInfo pinfo 
=  t.GetProperty(prname);
        MethodInfo method 
=  pinfo.GetSetMethod();
        Delegate dec 
=  Delegate.CreateDelegate( typeof (T), ( object )control, method);
        
return  dec  as  T;
    }
}

// [!]暂时放在这里,等有在用到的时候在重构
delegate   void  SetDataSource( object  o);


看完以上两个类的实现后,各位看官可能了解的我为何还要封装 WebView、WinView 这两层了
因为 winForm 和 ASP.net 控件、差异太大了,要做通用的只有这样了,否则 Presenter 就没法玩了
如果是单纯 Web 或 WinForm 大可不必这么郁闷;
Web 的不是代码少是还没写完懒得写了有些、DataGrid 和 GridView 看似都有 DataSource 不过又是
不是一个基类,又不是一个接口,郁闷
SetPropertyToDelegate 函数可以看看,是把属性转换为委托的
在使用次数比较多的时候、比直接反射快很多
专门对付函数或属性相同但不是一个基类接口的情况

其他那些 IView 接口的实现也都是差不多的;

【应用程序的代码】

【OpenSource.FlashElf.MVP.WinApp】 - WinForm 程序

namespace  OpenSource.FlashElf.MVP.WinApp
{
    
public   partial   class  Form1 : Form
    {
        DefaultPresenter _presenter;
        
public  Form1()
        {
            InitializeComponent();
            _presenter 
=   new  DefaultPresenter( new  UIDefault( this this .btnQuery,
                 
this .gvList,  this .txtQuery));
        }    
    }
}

【ViewSite】 - 网站

public   partial   class  _Default : System.Web.UI.Page
{
    
    
public  _Default()
    {
        
    }

    DefaultPresenter _presenter;
    
protected   override   void  OnInit(EventArgs e)
    {
        
        
base .OnInit(e);
        _presenter 
=   new  DefaultPresenter(  new  UIDefault( this , this .btnQuery
            ,
this .gvList, this .txtQuery));
    }
    
    
}

就是调用了一下 Presenter
Web Win 几乎一摸一样;就是 UIDefault 的名称空间不同
有人会问你怎么没实现 IView 项目里的接口?
这个问题......哎呀,MVP 没说一定要实现个什么接口吧
在说程序窗口上不是越少代码越好吗?外挂的就不算了吗?
我这里 UIDefault 就是 View 实现了 IView 里的接口了已经;

看一下 UIDefault 怎么实现的吧

namespace  OpenSource.FlashElf.MVP.WinView
{
    
public   class  UIDefault : IUIDefault
    {
        Form _page;
        UIDataBindControl _gridList;
        UIButton _btnQuery;
        UITextControl _txtBoxQuery;

        
public  UIDefault(Form page, Button btn, DataGridView grid, TextBox txtBox)
        {
            
this ._page  =  page;
            
this ._page.Load  += new  EventHandler(_page_Load);
            
            _gridList 
=   new  UIDataBindControl(grid);
            _btnQuery 
=   new  UIButton(btn);
            _txtBoxQuery 
=   new  UITextControl(txtBox);
        }

        
void   _page_Load( object  sender, EventArgs e)
        {
            
            
if (Load  != null )
                Load(sender,e); 
                        
        }
        
        
#region  IUIDefault 成员

        
public  IUIDataBindControl GridList
        {
            
get  {  return  _gridList; }
        }

        
public  IUIButton BtnGetData
        {
            
get  {  return  _btnQuery; }
        }

        
public  IUITextControl TextBoxQuery
        {
            
get  {  return  _txtBoxQuery; }
        }        
        
        
#endregion

        
#region  IView 成员

        
public   event  EventHandler Load;

        
#endregion
    }
}



namespace  OpenSource.FlashElf.MVP.WebView
{
    
public   class  UIDefault : IUIDefault
    {
        
public  Page _page;
        
public  UIDataBindControl _gridList;
        
public  UIButton _btnQuery;
        
public  UITextControl _txtBoxQuery;
        
        
public  UIDefault(Page page,Button btn,GridView grid,TextBox txtBox)
        {
            
this ._page  =  page;
            
this ._page.Load += new  EventHandler(_page_Load);
            
            _gridList 
=   new  UIDataBindControl(grid);
            _btnQuery 
=   new  UIButton(btn);
            _txtBoxQuery 
=   new  UITextControl(txtBox);
        }

        
void   _page_Load( object  sender, EventArgs e)
        {
            
if ( ! _page.IsPostBack)
            {
                
if (Load  != null )
                    Load(sender,e); 
            }            
        }
        
        
#region  IUIDefault 成员

        
public  IUIDataBindControl GridList
        {
            
get  {  return  _gridList; }
        }

        
public  IUIButton BtnGetData
        {
            
get  {  return  _btnQuery; }
        }

        
public  IUITextControl TextBoxQuery
        {
            
get  {  return  _txtBoxQuery; }
        }        
        
        
#endregion

        
#region  IView 成员

        
public   event  EventHandler Load;

        
#endregion

        
    }
}

做了一点点的处理,代码几乎是一样的、就是load 函数有些不同
感谢ms web win 都有 Load 事件都是 EventHandler 要不
就没法玩啦又要做特殊处理;

2) Presenter 部分
OpenSource.FlashElf.MVP.Presenter.DefaultPresenter

 

using  System;
using  System.Collections.Generic;
using  System.Text;
using  OpenSource.FlashElf.MVP.IView;
using  System.Data;
using  OpenSource.FlashElf.MVP.Model.ShareData;
using  OpenSource.FlashElf.MVP.Model.BILL.Manager;

namespace  OpenSource.FlashElf.MVP.Presenter
{
    
public   class  DefaultPresenter
    {    
        IUIDefault _view;
        
public  DefaultPresenter(IUIDefault view)
        {
            _view 
=  view;
            _view.Load 
+=   new  EventHandler(_view_Load);
            _view.BtnGetData.Click 
+=   new  EventHandler(BtnGetData_Click);
        }

        
void  BtnGetData_Click( object  sender, EventArgs e)
        {
            UserInfos datas 
=   new  UserInfos();
            
            
if (_view.TextBoxQuery.Text.Trim().Length != 0 )
            {
                
// 掉用 BILL
                UserInfo data  =  UserManager.GetUserInfoByLoginName( _view.TextBoxQuery.Text );
                datas.Add(data);
                _view.GridList.DataBind(datas);
            }
            
else
            {
                _view_Load(
null ,e);
                
            }
            
            
        }

        
void  _view_Load( object  sender, EventArgs e)
        {
            
// 掉用 BILL
            UserInfos datas  =  UserManager.GetUserInfoALL();
            _view.GridList.DataBind(datas);
        }
    }
}


终于可以到 Presenter 很失望也没多少代码?
demo 吗就3个控件所以能有多少代码

有人是不是要问 BILL 为啥都是静态的、谁说 BILL 不可以是静态的?
我是没看那个设计模式里写过 业务处理层,一定不能是静态的。
用什么方式要根据业务定吧?

3) 看看 Model
Model 这个词到底应该放在那里 PetShop 里明明就是个实体类
哎...,不过mvp 里却是整个的后台处理,郁闷“鸟”文真难理解啊...

先看BILL

using  System;
using  System.Collections.Generic;
using  System.Text;
using  OpenSource.FlashElf.MVP.Model.ShareData;
using  OpenSource.FlashElf.MVP.ServerUtility;
using  OpenSource.FlashElf.MVP.Model.IDAL.Manager;

namespace  OpenSource.FlashElf.MVP.Model.BILL.Manager
{
    
public   class  UserManager
    {
        
static   readonly  IUserManager _DAL;
        
        
static  UserManager()
        {
            
// 创建 IDAL
            _DAL  =  DecisionSimpleFactoryHelper.Create < IUserManager > ( " UserManagerDAL " " Manager " );
        }
        
        
public   static  UserInfos GetUserInfoALL()
        {
            UserInfos models 
=  _DAL.GetUserInfoALL();

            
return  models; 
        }
        
        
public   static  UserInfo GetUserInfoByLoginName( string  name)
        {
            UserInfo model 
=   null ;

            
if  ( string .Concat(name).Trim().Length  >   0  )
            {
                model 
=  _DAL.GetUserInfoByLoginName(name);
            }
            
else
            {
                
throw   new  ArgumentNullException( " Name " );
            }
            
            
return  model;
        }
    }
}


估计有人又会说,IUserManager 为啥也是静态的,一点也不oo?
 呵呵、省内存啊(不信自己试验)对于那种DAL层数据库用完就 close 的、而且没什么实例变量的
 应用比较适合
 而且 Web 你覆盖 dll 就会重新启动应用,不会存在什么问题
 我好几个应用都这么干的

DecisionSimpleFactoryHelper.Create 是啥?
 用他把,PetShop 那个工厂项目给省略了,我没感觉有啥不好的反正
 要用才是硬道理,而且这也不违反设计模式吧;

代码

namespace  OpenSource.FlashElf.MVP.ServerUtility
{
    
///   <summary>
    
///  简单工厂决策类
    
///   </summary>
     public   class  DecisionSimpleFactoryHelper
    {
        
const   string  Key  =   " SimpleFactoryConfig " ;
        
        
///   <summary>
        
///  从配置文件反射加载类
        
///   </summary>
        
///   <param name="AppSettingsKey"> 配置文件的 key </param>
        
///   <param name="className"> 类名 </param>
        
///   <param name="subNamespace"> 名称空间 </param>
        
///   <returns> 反射的对象 </returns>
         public   static   object  Create( string  AppSettingsKey,  string  className,  string  subNamespace)
        {
            
string  path  =  ConfigurationManager.AppSettings[AppSettingsKey];
            
string  classFillName;
            
if  (subNamespace  !=   null   &&  subNamespace.Length  >   0 )
                classFillName 
=   string .Concat(path,  " . " , subNamespace,  " . " , className);
            
else
                classFillName 
=   string .Concat(path,  " . " , className);

            
return  Assembly.Load(path).CreateInstance(classFillName);
        }

        
///   <summary>
        
///  读取 Key 下的 dal 类
        
///   </summary>
        
///   <param name="className"></param>
        
///   <param name="subNamespace"></param>
        
///   <returns></returns>
         public   static   object  Create( string  className,  string  subNamespace)
        {
            
return  Create(DecisionSimpleFactoryHelper.Key, className, subNamespace);

        }
        
///   <summary>
        
///  
        
///   </summary>
        
///   <param name="className"></param>
        
///   <returns></returns>
         static   object  Create( string  className)
        {
            
return  Create(className,  null );
        }
        
///   <summary>
        
///  范型实现
        
///   </summary>
        
///   <typeparam name="T"></typeparam>
        
///   <param name="className"></param>
        
///   <param name="subNamespace"></param>
        
///   <returns></returns>
         public   static  T Create < T > ( string  className,  string  subNamespace)
        {
            
object  o  =  Create(className, subNamespace);
            
return  (T)o;
        }

        
///   <summary>
        
///  范型实现
        
///   </summary>
        
///   <typeparam name="T"></typeparam>
        
///   <param name="className"></param>
        
///   <returns></returns>
         public   static  T Create < T > ( string  className)
        {
            
return  Create < T > (className,  null );
        }

    }
}

看看配置文件

Web 的

<appSettings>
 <add key="SimpleFactoryConfig" value="OpenSource.FlashElf.MVP.Model.TestDAL"/>  
</appSettings>


Win 的

<appSettings>
 <add key="SimpleFactoryConfig" value="OpenSource.FlashElf.MVP.Model.WebServiceDAL"/>
</appSettings>

现在解决方案里是这样的 DAL ,正好把两个 DAL 都配置上
其实怎么配都一样的都,配置成 WebServiceDAL 或 TestDAL 也没问题
我就是为了 demo


好了基本差不多了,剩下的写不动了我也累了、MVP 部分都讲了基本
剩下的都是 简单工程模式的东西了。
代码都没有说的多,剩下的代码就和 PetShop 差不多了
没有用任何数据库,都是临时生成的数据。

心得
 如果没有代码生成器,写这种模式太累了,n个工程虽然代码不多,不过要在实际环境一定能累死人
 而且会出现各种各样的问题,要建立在良好的设计之上是必须的(我就是因为没设计直接敲代码改来改去的)

 个人认为 mvp 模式【不适合】容易更改的项目,或很小的项目
 不过一心想把小的往大了做的没办法。

这么做 mvp 也不知道对不对,第一次写这种模式、反正已经发出去了、各位看官提点意见
我这就是一个demo 可不要太苛刻了呵呵。
错别字一定很多,慢慢改吧先发了在说;^_^.

最后:
代码下载 (61k)

本文仅发布于: http://www.cnblogs.com/ 、转载请注明,作者等相关信息

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值