MVP模式及实例
最近在关注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层,该层代码与三层架构的实体层是相同的。
1namespace Model
2{
3 using System;
4 public class StaffInfo
5 {
6 private string _stfId;
7 private string _stfName;
8 private string _sex;
9 private string _native;
10 private string _birthDay;
11
12 public StaffInfo()
13 {
14 }
15
16 public StaffInfo(string stfId, string stfName, string sex, string native, string birthDay)
17 {
18 this._stfId = stfId;
19 this._stfName = stfName;
20 this._sex = sex;
21 this._native = native;
22 this._birthDay = birthDay;
23 }
24 /**//// <summary>
25 /// 员工工号
26 /// </summary>
27 public string StfId
28 {
29 get { return _stfId; }
30 set { _stfId = value; }
31 }
32 /**//// <summary>
33 /// 员工姓名
34 /// </summary>
35 public string StfName
36 {
37 get { return _stfName; }
38 set { _stfName = value; }
39 }
40 /**//// <summary>
41 /// 性别
42 /// </summary>
43 public string Sex
44 {
45 get { return _sex; }
46 set { _sex = value; }
47 }
48 /**//// <summary>
49 /// 籍贯
50 /// </summary>
51 public string Native
52 {
53 get { return _native; }
54 set { _native = value; }
55 }
56 /**//// <summary>
57 /// 出生年月
58 /// </summary>
59 public string BirthDay
60 {
61 get { return _birthDay; }
62 set { _birthDay = value; }
63 }
64 }
65}
第二步,定义员工的页面接口 IStaffDetailView ,在页面中,一般有一些常用的自定义函数,这时我们可以把它定义在父接口,然后用一个页面父类去实现它,如下面的MessageBox这个函数,就可以用这样的方式。那么我们页面接口就可以继承这个父接口,这样可以增强代码的重用性。
1namespace Presenter
2{
3 using System;
4 using Model;
5 public interface IStaffDetailView
6 {
7 /**//// <summary>
8 /// 员工工号
9 /// </summary>
10 string StfId { get;}
11
12 /**//// <summary>
13 /// 员工姓名
14 /// </summary>
15 string StfName { get;}
16
17 /**//// <summary>
18 /// 性别
19 /// </summary>
20 string Sex { get;}
21
22 /**//// <summary>
23 /// 籍贯
24 /// </summary>
25 string Native { get;}
26
27 /**//// <summary>
28 /// 出生年月
29 /// </summary>
30 string BirthDay { get;}
31
32 /**//// <summary>
33 /// 绑定员工信息
34 /// </summary>
35 /// <param name="infos"></param>
36 void BindDetail(StaffInfo> infos);
37
38 /**//// <summary>
39 /// 更新员工信息事件
40 /// </summary>
41 event EventHandler<EventArgs> UpdateEvent;
42 }
43}
第三步,定义员工数据访问类,我这里只是给一个示例,真实的项目中当然要封装一下才行。
1namespace DataAccess
2{
3 using System;
4 using System.Data;
5 using System.Data.SqlClient;
6 using Model;
7
8 public class Staff
9 {
10 private static string connectionString = "server=.;database=db;user id=sa;password=password";
11
12 /**//// <summary>
13 /// 获取员工信息
14 /// </summary>
15 /// <param name="stfId">员工工号</param>
16 /// <returns></returns>
17 private StaffInfo GetStaffDetail(string stfId)
18 {
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 {
26 new SqlParameter("@stfId",SqlDbType.VarChar,10)
27 };
28
29 paras[0].Value = stfId;
30
31 foreach (SqlParameter para in paras)
32 {
33 da.SelectCommand.Parameters.Add(para);
34 }
35 try
36 {
37 con.Open();
38 da.Fill(dt);
39 con.Close();
40 StaffInfo info = new StaffInfo();
41 foreach (DataRow dr in dt.Rows)
42 {
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 {
52 throw new Exception(ex.Message);
53 }
54 }
55
56 /**//// <summary>
57 /// 更新员工信息
58 /// </summary>
59 /// <param name="info"></param>
60 /// <returns></returns>
61 private bool UpdateStaff(StaffInfo info)
62 {
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 {
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
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
81 foreach (SqlParameter para in paras)
82 {
83 cmd.Parameters.Add(para);
84 }
85 try
86 {
87 con.Open();
88 cmd.EndExecuteNonQuery();
89 con.Close();
90 return true;
91 }
92 catch
93 {
94 return false;
95 }
96 }
97 }
98}
第四步,定义员工Presenter层
1public class StaffDetailPresenter
2 {
3 private IStaffDetailView _view;
4
5 public StaffDetailPresenter(IStaffDetailView view)
6 {
7 this._view = view;
8 }
9
10 /**//// <summary>
11 /// 初始化页面
12 /// </summary>
13 /// <param name="IsPostBack">首次提交,WebFrom中,此值为IsPostBack,WinFrom中,此值为false</param>
14 public void InitializeView(bool IsPostBack)
15 {
16 if(!IsPostBack)
17 BindDetail();
18 this._view.UpdateEvent += new EventHandler<EventArgs>(View_UpdateEvent);
19 }
20
21 private void BindDetail()
22 {
23 this._view.BindDetail(new Staff(this._view.StfId));
24 }
25
26 private void View_UpdateEvent(object sender, EventArgs e)
27 {
28 if (new Staff().UpdateStaff(new Staff(this._view.StfId, this._view.StfName, this._view.Sex, this._view.Native, this._view.BirthDay)))
29 {
30 this.BindDetail();
31 this._view.MessageBox("更新成功");
32 }
33 else
34 this._view.MessageBox("更新失败!");
35 }
36 }
第五步:实现View接口,我这里以WebFrom为例,在页面中做了数据绑定与更新数据事件两个操作,还有如绑定GridView等等操作,大家可以探讨。
1namespace Web
2{
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
18 public partial class StaffDetail : Page, IStaffDetailView
19 {
20 实现接口#region 实现接口
21 /**//// <summary>
22 /// 员工工号
23 /// </summary>
24 public string StfId { get { if (object.Equals(Request.QueryString["stfId"], null)) return null; return Request.QueryString["stfId"].ToString(); } }
25
26 /**//// <summary>
27 /// 员工姓名
28 /// </summary>
29 public string StfName { get { return txtStfName.Text; } }
30
31 /**//// <summary>
32 /// 性别
33 /// </summary>
34 public string Sex { get { return ddlSex.SelectValue; } }
35
36 /**//// <summary>
37 /// 籍贯
38 /// </summary>
39 public string Native { get { return txtNative.Text; } }
40
41 /**//// <summary>
42 /// 出生年月
43 /// </summary>
44 public string BirthDay { get { return txtBirthDay.Text; } }
45
46 /**//// <summary>
47 /// 绑定员工信息
48 /// </summary>
49 /// <param name="info"></param>
50 public void BindDetail(StaffInfo info)
51 {
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
59 /**//// <summary>
60 /// 弹出对话框
61 /// </summary>
62 /// <param name="msg">对话框消息</param>
63 public void MessageBox(string msg)
64 {
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
69 /**//// <summary>
70 /// 更新员工信息事件
71 /// </summary>
72 public event EventHandler<EventArgs> UpdateEvent;
73 #endregion
74
75 protected void Page_Load(object sender, EventArgs e)
76 {
77 StaffDetailPresenter p = new StaffDetailPresenter(this);
78 p.InitializeView(IsPostBack);
79 }
80
81 protected void btn_Update(object sender, EventArgs e)
82 {
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层,它的作用在于对数据库进行操作。
欢迎大家来批评指正。