最近在关注MVP模式的文章,看了张子阳的 MVP 模式实例解析和李春雷的MVP模式最佳实践(1)—MVP模式简介 ,自己也想弄一个来试试。
关于MVP模式的概念,网上很多,在此摘抄一段吧。
MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。作为一种新的模式,MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会从直接Model中读取数据而不是通过 Controller。Alex在他的blog中对于这两者之间的比较很直观也比较清楚,原文可以下面的地址找到:http://ameleta.spaces.live.com/blog/cns!5F6316345A821420!163.entry
准确的说,MVP模式和三层架构相比,MVP模式的编码量比较大,我个人认为普通的项目如果不涉及到WinFrom 与WebFrom 之间的转换,最好不要用MVP的模式。
下面我就以我所理解的MVP模式写一个小实例,是关于某个员工基本信息的数据绑定与编辑的操作。
在员工基本信息的编辑界面中,需要展示的信息包括员工的工号,姓名,性别,籍贯,出生年月等等,在此实例中我只展示这五个字段,一个绑定员工信息的方法,以及一个更新员工信息的事件。
首先,定义员工的Model层,该层代码与三层架构的实体层是相同的。
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Code
1
namespace Model
2![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
3
using System;
4
public class StaffInfo
5![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
6
private string _stfId;
7
private string _stfName;
8
private string _sex;
9
private string _native;
10
private string _birthDay;
11![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
12
public StaffInfo()
13![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
14
}
15![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
16
public StaffInfo(string stfId, string stfName, string sex, string native, string birthDay)
17![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
18
this._stfId = stfId;
19
this._stfName = stfName;
20
this._sex = sex;
21
this._native = native;
22
this._birthDay = birthDay;
23
}
24![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
25
/// 员工工号
26
/// </summary>
27
public string StfId
28![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
29![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return _stfId; }
30![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{ _stfId = value; }
31
}
32![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
33
/// 员工姓名
34
/// </summary>
35
public string StfName
36![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
37![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return _stfName; }
38![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{ _stfName = value; }
39
}
40![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
41
/// 性别
42
/// </summary>
43
public string Sex
44![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
45![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return _sex; }
46![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{ _sex = value; }
47
}
48![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
49
/// 籍贯
50
/// </summary>
51
public string Native
52![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
53![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return _native; }
54![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{ _native = value; }
55
}
56![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
57
/// 出生年月
58
/// </summary>
59
public string BirthDay
60![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
61![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return _birthDay; }
62![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{ _birthDay = value; }
63
}
64
}
65
}
第二步,定义员工的页面接口 IStaffDetailView ,在页面中,一般有一些常用的自定义函数,这时我们可以把它定义在父接口,然后用一个页面父类去实现它,如下面的MessageBox这个函数,就可以用这样的方式。那么我们页面接口就可以继承这个父接口,这样可以增强代码的重用性。
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Code
1
namespace Presenter
2![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
3
using System;
4
using Model;
5
public interface IStaffDetailView
6![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
7![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
8
/// 员工工号
9
/// </summary>
10![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
string StfId
{ get;}
11![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
12![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
13
/// 员工姓名
14
/// </summary>
15![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
string StfName
{ get;}
16![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
17![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
18
/// 性别
19
/// </summary>
20![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
string Sex
{ get;}
21![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
22![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
23
/// 籍贯
24
/// </summary>
25![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
string Native
{ get;}
26![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
27![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
28
/// 出生年月
29
/// </summary>
30![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
string BirthDay
{ get;}
31![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
32![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
33
/// 绑定员工信息
34
/// </summary>
35
/// <param name="infos"></param>
36
void BindDetail(StaffInfo> infos);
37![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
38![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
39
/// 更新员工信息事件
40
/// </summary>
41
event EventHandler<EventArgs> UpdateEvent;
42
}
43
}
第三步,定义员工数据访问类,我这里只是给一个示例,真实的项目中当然要封装一下才行。
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Code
1
namespace DataAccess
2![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
3
using System;
4
using System.Data;
5
using System.Data.SqlClient;
6
using Model;
7![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
8
public class Staff
9![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
10
private static string connectionString = "server=.;database=db;user id=sa;password=password";
11![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
12![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
13
/// 获取员工信息
14
/// </summary>
15
/// <param name="stfId">员工工号</param>
16
/// <returns></returns>
17
private StaffInfo GetStaffDetail(string stfId)
18![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
19
string cmdText = "select stfName,sex,native,birthDay from tb_Staff where stfId=@stfId";
20
SqlConnection con = new SqlConnection(connectionString);
21
SqlDataAdapter da = new SqlDataAdapter(cmdText,con);
22
DataTable dt = new DataTable();
23
da.SelectCommand.CommandType = CommandType.Text;
24
SqlParameter[] paras = new SqlParameter[]
25![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
26
new SqlParameter("@stfId",SqlDbType.VarChar,10)
27
};
28![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
29
paras[0].Value = stfId;
30![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
31
foreach (SqlParameter para in paras)
32![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
33
da.SelectCommand.Parameters.Add(para);
34
}
35
try
36![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
37
con.Open();
38
da.Fill(dt);
39
con.Close();
40
StaffInfo info = new StaffInfo();
41
foreach (DataRow dr in dt.Rows)
42![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
43
info.StfName = dr[0].ToString();
44
info.Sex = dr[1].ToString();
45
info.Native = dr[2].ToString();
46
info.BirthDay = dr[3].ToString();
47
}
48
return info;
49
}
50
catch (Exception ex)
51![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
52
throw new Exception(ex.Message);
53
}
54
}
55![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
56![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
57
/// 更新员工信息
58
/// </summary>
59
/// <param name="info"></param>
60
/// <returns></returns>
61
private bool UpdateStaff(StaffInfo info)
62![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
63
string cmdText = "update tb_Staff set stfName=@stfName,sex=@sex,native=@native,birthDay=@birthDay where stfId=@stfId";
64
SqlConnection con = new SqlConnection(connectionString);
65
SqlCommand cmd = new SqlCommand(cmdText, con);
66
SqlParameter[] paras = new SqlParameter[]
67![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
68
new SqlParameter("@stfId",SqlDbType.VarChar,10),
69
new SqlParameter("@stfName",SqlDbType.NVarChar,20),
70
new SqlParameter("@sex",SqlDbType.NVarChar,1),
71
new SqlParameter("@native",SqlDbType.NVarChar,20),
72
new SqlParameter("@birthDay",SqlDbType.VarChar,10)
73
};
74![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
75
paras[0].Value = info.StfId;
76
paras[1].Value = info.StfName;
77
paras[2].Value = info.Sex;
78
paras[3].Value = info.Native;
79
paras[4].Value = info.BirthDay;
80![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
81
foreach (SqlParameter para in paras)
82![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
83
cmd.Parameters.Add(para);
84
}
85
try
86![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
87
con.Open();
88
cmd.EndExecuteNonQuery();
89
con.Close();
90
return true;
91
}
92
catch
93![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
94
return false;
95
}
96
}
97
}
98
}
第四步,定义员工Presenter层
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Code
1
public class StaffDetailPresenter
2![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
3
private IStaffDetailView _view;
4![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
5
public StaffDetailPresenter(IStaffDetailView view)
6![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
7
this._view = view;
8
}
9![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
10![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
11
/// 初始化页面
12
/// </summary>
13
/// <param name="IsPostBack">首次提交,WebFrom中,此值为IsPostBack,WinFrom中,此值为false</param>
14
public void InitializeView(bool IsPostBack)
15![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
16
if(!IsPostBack)
17
BindDetail();
18
this._view.UpdateEvent += new EventHandler<EventArgs>(View_UpdateEvent);
19
}
20![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
21
private void BindDetail()
22![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
23
this._view.BindDetail(new Staff(this._view.StfId));
24
}
25![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
26
private void View_UpdateEvent(object sender, EventArgs e)
27![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
28
if (new Staff().UpdateStaff(new Staff(this._view.StfId, this._view.StfName, this._view.Sex, this._view.Native, this._view.BirthDay)))
29![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
30
this.BindDetail();
31
this._view.MessageBox("更新成功");
32
}
33
else
34
this._view.MessageBox("更新失败!");
35
}
36
}
第五步:实现View接口,我这里以WebFrom为例,在页面中做了数据绑定与更新数据事件两个操作,还有如绑定GridView等等操作,大家可以探讨。
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Code
1
namespace Web
2![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
3
using System;
4
using System.Data;
5
using System.Data.SqlClient;
6
using System.Configuration;
7
using System.Collections;
8
using System.Collections.Generic;
9
using System.Web;
10
using System.Web.Security;
11
using System.Web.UI;
12
using System.Web.UI.WebControls;
13
using System.Web.UI.WebControls.WebParts;
14
using System.Web.UI.HtmlControls;
15
using Presenter;
16
using Model;
17![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
18
public partial class StaffDetail : Page, IStaffDetailView
19![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
20![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
实现接口#region 实现接口
21![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
22
/// 员工工号
23
/// </summary>
24![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public string StfId
{ get
{ if (object.Equals(Request.QueryString["stfId"], null)) return null; return Request.QueryString["stfId"].ToString(); } }
25![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
26![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
27
/// 员工姓名
28
/// </summary>
29![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public string StfName
{ get
{ return txtStfName.Text; } }
30![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
31![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
32
/// 性别
33
/// </summary>
34![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public string Sex
{ get
{ return ddlSex.SelectValue; } }
35![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
36![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
37
/// 籍贯
38
/// </summary>
39![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public string Native
{ get
{ return txtNative.Text; } }
40![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
41![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
42
/// 出生年月
43
/// </summary>
44![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public string BirthDay
{ get
{ return txtBirthDay.Text; } }
45![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
46![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
47
/// 绑定员工信息
48
/// </summary>
49
/// <param name="info"></param>
50
public void BindDetail(StaffInfo info)
51![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
52
lbStfId.Text = info.StfId;
53
txtStfName.Text = info.StfName;
54
ddlSex.SelectValue = info.Sex;
55
txtNative.Text = info.Native;
56
txtBirthDay.Text = info.BirthDay;
57
}
58![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
59![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
60
/// 弹出对话框
61
/// </summary>
62
/// <param name="msg">对话框消息</param>
63
public void MessageBox(string msg)
64![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
65
string script = string.Format("<script language='javascript' type='text/javascript' defer>window.alert('{0}');</script>", msg);
66
this.ClientScript.RegisterStartupScript(this.GetType(), "系统提示", script);
67
}
68![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
69![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
70
/// 更新员工信息事件
71
/// </summary>
72
public event EventHandler<EventArgs> UpdateEvent;
73
#endregion
74![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
75
protected void Page_Load(object sender, EventArgs e)
76![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
77
StaffDetailPresenter p = new StaffDetailPresenter(this);
78
p.InitializeView(IsPostBack);
79
}
80![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
81
protected void btn_Update(object sender, EventArgs e)
82![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
83
if (this.UpdateEvent != null)
84
this.UpdateEvent(sender, e);
85
}
86
}
87
88
}
以上是整个实例的代码。这个实例与李春雷的实例的大体结构类似,不同之处在于以下两点
1、视图接口中增加了按钮事件,这个思路来源于http://www.cnblogs.com/chinasf/archive/2006/12/20/597987.html;
2、明确了Model层与Control层的职责,在Model层的设计中我采用了与三层框架的实体层相同的方式,DataAccess作为Control层,它的作用在于对数据库进行操作。
欢迎大家来批评指正。
示例别的示例
转自:http://www.cnblogs.com/easyit/archive/2009/05/12/1454786.html