• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
flashelf
博客园    首页    新随笔    联系   管理    订阅  订阅

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

设计模式学习:Model View Presenter (MVP) 自己写的没用什么框架、就是个 demo 。

设计模式学习: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/ 、转载请注明,作者等相关信息

posted @ 2008-06-23 00:18  曲滨*銘龘鶽  阅读(3549)  评论(8)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3