MVP+WCF+三层结构搭建项目框架(上)
最近,我一直在重构之前做的一个项目,在这个过程中感慨万千。原先的项目是一个运用了WCF的C/S系统,在客户端运用了MVC模式,但MVC的View、Model耦合以及WCF端分布式欠佳等问题让我有了重构的想法,经过了一段时间的改造,逐渐形成了MVP+三层结构+WCF的面向服务的程序架构。在这里我把我的想法写成了一个例子,供大家参考。
在正式开始讲解之前,我必须得感谢Artech、代震军等诸多大虾,他们的文章给了我很大的启发。
我写的这个例子是关于博客管理的,逻辑很简单,就是用户发表文章、发表评论,管理员可以对用户进行管理。让我们先从MVP的运用开始讲起。
MVP与MVC的选择
关于MVP和MVC,我只谈谈在重构过程中的看法。在经典的MVC中,View会通过Controller调用Model中的方法,Model被更新后会立即通知View,因此View与Model还是存在一定程度的耦合的,Controller也只是作为一个简单的消息分发器,View与Controller也紧紧的贴合好像是一个整体。而在MVP中,Presenter代替Controller而出现,成为View与Model解耦的关键,在MVP中,Presenter掌管大权,View的请求提交给Presenter,由Presenter调用Model,Model更新后的状态再返回给Presenter,由Presenter控制View进行相应的显示。如此,Model与View被完全解耦,View与Controller也形成了单向依赖。MVC与MVP可以说各有特点,但我更倾向于MVP。
关于MVC和MVP,给大家看这两张图就很明白了。对于刚接触MVP的朋友可能不知道View、Presenter和Model中具体该实现哪些东西,我会在用实例讲解的时候告诉大家我的想法。
MVP实战运用
在确定使用MVP模式之后,我给我的博客程序安排了如下几个项目:
Main:主程序入口点。
Common:存放委托和公共组件等。
Model:MVP中的M,注意区别于三层中的Model。
Presenter:MVP中的P。
View:MVP中的V。
DTO:这个项目其实和三层中的Model作用是一样的,但是我为了区别于MVP中的Model,把它叫做DTO,其实它的作用就是一个DTO。
项目之间的引用关系是这样的:
Main直接调用Presenter启动程序。Presenter与View,Presenter和Model分别都是单向引用,而View和Model完全没有任何联系。View和Presenter需要引用Common,使用其中的一些公共组件,而DTO作为存放数据传输对象的项目View、Presenter和Model必须都要引用。
在搭建好项目框架之后,我们可以先从Model入手,因为Model是业务逻辑的所在地,是数据的提供者,它完全基于用例的。按照本例需求,分析出三个实体,分别是User(用户),Note(文章),Comment(评论),我们就以User为例,写一个Model。代码如下所示:
1 /// <summary> 2 /// The interface of user model 3 /// </summary> 4 public interface IUserGroup 5 { 6 #region --Methods-- 7 /// <summary> 8 /// Get all users 9 /// </summary>10 /// <returns>Users</returns>11 IList<User> GetAllUsers();12 13 /// <summary>14 /// Get user by user id15 /// </summary>16 /// <param name="id">the user id</param>17 /// <returns>User</returns>18 User GetUserById(string id);19 20 /// <summary>21 /// Update user22 /// </summary>23 /// <param name="user">the user</param>24 void UpdateUser(User user);25 26 /// <summary>27 /// Delete user by user id28 /// </summary>29 /// <param name="userId">the user id</param>30 void DeleteUser(string userId);31 #endregion32 }
这是一个用于处理User业务逻辑的接口,其中使用到的User类就是DTO中的User实体。Model中把接口公开出来供Presenter调用就可以了,Note和Comment的代码也类似,P-M之间的交互还是比较简单的。
OK,设计完Model,我们再来看看界面如何呈现,我的想法如图所示:
界面很丑陋,大家就凑合看吧。窗口就一个,有一个DataGridView用来显示所有用户,当选中一行时在下面的TextBox中显示用户的详细信息。我们所能看到的这个UI界面就是MVP中View了。设计View时需要注意的是,View一定要针对接口设计,不要针对实现,因为我们的设想是将View做成Passive View,一定要让Presenter依赖于一个抽象的View。
在本例中,我将这个界面分解为两个View,一个是显示用户详细信息的主窗体(IUserControlView),一个是显示所有用户信息的列表(IGridView),可以看到IGridView是IUserControlView的一部分。代码如下所示:
1 /// <summary> 2 /// The interface of UserControlView 3 /// </summary> 4 public interface IUserControlView 5 { 6 #region --Event-- 7 /// <summary> 8 /// Occurs when the view was loaded 9 /// </summary>10 event EventHandler OnViewLoad;11 #endregion12 13 /// <summary>14 /// set the UserGridView15 /// </summary>16 IGridView UserGridView17 {18 set;19 }20 21 #region --Methods--22 /// <summary>23 /// Initialize the components of this view24 /// </summary>25 void Initialize();26 27 /// <summary>28 /// Show one user information at the interface29 /// </summary>30 /// <param name="user"></param>31 void ShowUserInfo(User user);32 33 /// <summary>34 /// Show alert form35 /// </summary>36 /// <param name="message">Messages should be shown</param>37 void Alert(string message);38 #endregion39 }
1 /// <summary> 2 /// The interface of Grid View 3 /// </summary> 4 public interface IGridView 5 { 6 #region --Event-- 7 /// <summary> 8 /// Occurs when a user was selected 9 /// </summary>10 event UserEventHandler OnUserSelected;11 /// <summary>12 /// Occurs when a user is begin to be edited13 /// </summary>14 event UserEventHandler OnUserBeginEdit;15 #endregion16 17 #region --Properties--18 /// <summary>19 /// 设置此View相对于父View的位置20 /// </summary>21 Point ViewLocation22 {23 set;24 }25 #endregion26 27 #region --Methods--28 /// <summary>29 /// bind data to this grid30 /// </summary>31 void BindData(IList<User> users);32 #endregion33 }
实现代码如下:
1 public class UserGridView : DataGridView, IGridView2 public class UserDetailForm : Form, IUserControlView
View实现代码的细节我就不贴了,在文章最后会提供示例的下载链接。
需要注意的是,View既然是针对抽象设计,接口中就不能暴露任何UI实现的细节。
现在,再来看看Presenter如何设计,一般来说一个View就有一个相对应的Presenter来控制,Presenter代码如下所示:
1 /// <summary> 2 /// The interface of GridPresenter 3 /// </summary> 4 public interface IGridPresenter 5 { 6 #region --Event-- 7 /// <summary> 8 /// Occurs when a user was selected 9 /// </summary>10 event UserEventHandler OnUserSelected;11 /// <summary>12 /// Occurs when a user is begin to be edited13 /// </summary>14 event UserEventHandler OnUserBeginEdit;15 #endregion16 17 #region --Properties--18 /// <summary>19 /// Get the view20 /// </summary>21 IGridView View22 {23 get;24 }25 #endregion26 27 #region --Methods--28 /// <summary>29 /// Show a group users data30 /// </summary>31 /// <param name="users"></param>32 void ShowUsers(IList<User> users);33 #endregion34 }
1 /// <summary> 2 /// The interface of UserControlPresenter 3 /// </summary> 4 public interface IUserControlPresenter 5 { 6 /// <summary> 7 /// Show the mian view.Start the application 8 /// </summary> 9 void Run();10 }
Presenter对View的引用是单向的,View不知道哪个Presenter在用它,View也无法访问到Presenter。我们让Presenter订阅View中的事件以响应View的请求。View是被动的,需要由Presenter控制,因此在Presenter实例化的时候同时实例化相应的View。
IGridPresenter的实现代码如下所示:
1 public class UserGridPresenter : IGridPresenter 2 { 3 #region --Event-- 4 /// <summary> 5 /// Occurs when a user was selected 6 /// </summary> 7 public event UserEventHandler OnUserSelected; 8 /// <summary> 9 /// Occurs when a user is begin to be edited 10 /// </summary> 11 public event UserEventHandler OnUserBeginEdit; 12 #endregion 13 14 #region --Fields-- 15 private IGridView mView; 16 #endregion 17 18 #region --Properties-- 19 /// <summary> 20 /// Get the GridView 21 /// </summary> 22 public IGridView View 23 { 24 get { return mView; } 25 } 26 #endregion 27 28 #region --Constructor-- 29 /// <summary> 30 /// Default constructor 31 /// </summary> 32 public UserGridPresenter() 33 { 34 mView = new UserGridView(); 35 AttachToUserGridView(mView); 36 } 37 #endregion 38 39 #region --Public Methods-- 40 /// <summary> 41 /// show user data 42 /// </summary> 43 /// <param name="users"></param> 44 public void ShowUsers(IList<User> users) 45 { 46 mView.BindData(users); 47 } 48 #endregion 49 50 #region --Private Methods-- 51 /// <summary> 52 /// Attach to the UserGridView 53 /// </summary> 54 /// <param name="view"></param> 55 private void AttachToUserGridView(IGridView view) 56 { 57 if (view != null) 58 { 59 view.OnUserSelected += new UserEventHandler(UserGridView_OnUserSelected); 60 view.OnUserBeginEdit += new UserEventHandler(UserGridView_OnUserBeginEdit); 61 } 62 } 63 #endregion 64 65 #region --Event Methods-- 66 /// <summary> 67 /// Occurs when the OnUserSelected event in UserGridView was raised 68 /// </summary> 69 private void UserGridView_OnUserSelected(object sender, UserEventArgs e) 70 { 71 RaiseOnUserSelected(e.UserValue); 72 } 73 74 /// <summary> 75 /// Occurs when the OnUserBeginEdit event in UserGridView was raised 76 /// </summary> 77 private void UserGridView_OnUserBeginEdit(object sender, UserEventArgs e) 78 { 79 RaiseOnUserBeginEdit(e.UserValue); 80 } 81 #endregion 82 83 #region --Raise Event Methods-- 84 /// <summary> 85 /// Raise the OnUserSelected event 86 /// </summary> 87 /// <param name="user"></param> 88 private void RaiseOnUserSelected(User user) 89 { 90 UserEventHandler handler = OnUserSelected; 91 if (handler != null) 92 { 93 UserEventArgs e = new UserEventArgs(); 94 e.UserValue = user; 95 handler(this, e); 96 } 97 } 98 99 /// <summary>100 /// Raise the OnUserBeginEdit event101 /// </summary>102 /// <param name="user"></param>103 private void RaiseOnUserBeginEdit(User user)104 {105 UserEventHandler handler = OnUserBeginEdit;106 if (handler != null)107 {108 UserEventArgs e = new UserEventArgs();109 e.UserValue = user;110 handler(this, e);111 }112 }113 #endregion114 }
IUserControlPresenter的实现代码如下所示:
1 public class UserControlPresenterBase : IUserControlPresenter 2 { 3 #region --Fields-- 4 private IGridPresenter mGridPresenter; 5 private IUserControlView mUserControlView; 6 #endregion 7 8 #region --Constructor-- 9 /// <summary> 10 /// Default constructor 11 /// </summary> 12 public UserControlPresenterBase() 13 { 14 Initialize(); 15 } 16 #endregion 17 18 #region --Public Methods-- 19 /// <summary> 20 /// Show the mian view.Start the application 21 /// </summary> 22 public void Run() 23 { 24 ServiceCollection.LoadServer(); 25 //Run as main form 26 Application.Run(mUserControlView as UserDetailForm); 27 } 28 #endregion 29 30 #region --Private Methods-- 31 /// <summary> 32 /// Initialize this presenter 33 /// </summary> 34 private void Initialize() 35 { 36 mUserControlView = new UserDetailForm(); 37 mGridPresenter = new UserGridPresenter(); 38 mUserControlView.UserGridView = mGridPresenter.View; 39 //The UI initialize method should be executed until all sub views was assigned 40 mUserControlView.Initialize(); 41 AttachToUserControlView(mUserControlView); 42 AttachToUserGridPresenter(mGridPresenter); 43 } 44 45 /// <summary> 46 /// Attach to the UserControlView 47 /// </summary> 48 /// <param name="view"></param> 49 private void AttachToUserControlView(IUserControlView view) 50 { 51 if (view != null) 52 { 53 view.OnViewLoad += new EventHandler(UserControlView_OnViewLoad); 54 } 55 } 56 57 /// <summary> 58 /// Attach to the UserGridPresenter 59 /// </summary> 60 /// <param name="presenter"></param> 61 private void AttachToUserGridPresenter(IGridPresenter presenter) 62 { 63 if (presenter != null) 64 { 65 presenter.OnUserSelected += new UserEventHandler(UserGridPresenter_OnUserSelected); 66 presenter.OnUserBeginEdit += new UserEventHandler(UserGridPresenter_OnUserBeginEdit); 67 } 68 } 69 70 /// <summary> 71 /// Show the view to edit a user 72 /// </summary> 73 /// <param name="user"></param> 74 private void ShowEditUserView(User user) 75 { 76 IEditUserPresenter presenter = new EditUserPresenter(); 77 presenter.OnUserEdited += new UserEventHandler(EditUserPresenter_OnUserEdited); 78 presenter.ShowView(user); 79 } 80 81 /// <summary> 82 /// Show all users 83 /// </summary> 84 private void ShowAllUsers() 85 { 86 IWS_Blog blogService = ServiceCollection.BlogService; 87 IList<User> userList = blogService.GetAllUsers(); 88 mGridPresenter.ShowUsers(userList); 89 } 90 #endregion 91 92 #region --Event Methods-- 93 /// <summary> 94 /// Occurs when the UserControlView was loaded 95 /// </summary> 96 private void UserControlView_OnViewLoad(object sender, EventArgs e) 97 { 98 ShowAllUsers(); 99 }100 101 /// <summary>102 /// Occurs when the OnUserSelected event in UserGridPresenter was raised103 /// </summary>104 private void UserGridPresenter_OnUserSelected(object sender, UserEventArgs e)105 {106 mUserControlView.ShowUserInfo(e.UserValue);107 }108 109 /// <summary>110 /// Occurs when the OnUserBeginEdit event in UserGridPresenter was raised111 /// </summary>112 private void UserGridPresenter_OnUserBeginEdit(object sender, UserEventArgs e)113 {114 ShowEditUserView(e.UserValue);115 }116 117 /// <summary>118 /// Occurs when a user edit finished119 /// </summary>120 private void EditUserPresenter_OnUserEdited(object sender, UserEventArgs e)121 {122 ShowAllUsers();123 }124 #endregion125 }
初始化加载所有用户信息的流程是这样的:当UserControlView加载时,UserControlPresenter随之响应并调用UserModel中的GetAllUsers方法。UserControlPresenter获取到数据后,再调用GridPresenter中的ShowUsers方法,将查询的数据绑定到GridView上,这时便看到了数据。在这个过程中,View提交请求是通过事件响应实现的,只要涉及到与数据相关的请求都必须要提交到Presenter中,由Presenter来决定下一步该做什么,View中则不能包含任何与数据相关的业务逻辑。从这个流程中我们也可以看到Presenter的掌控地位,他属于指手划脚的那类人,只是在告诉别人需要做什么,但自己却不会亲自动手。
大家可能注意到了,我把UserControlView中的Initialize方法公开在接口中,并没有在UserControlView构造时执行,这是因为UserControlView依赖于一个抽象的GridView,在构造时GridView还没有被注入,显然在这个时候初始化会出现未将对象引用设置到对象的实例的异常。因此我把UserControlView的初始化工作交给Presenter处理,将初始化的时机延后到所有依赖注入完成,而View不会主动执行Initialize。
IUserControlPresenter已经是最顶层的Presenter了,它只需要公开出接口供Main调用启动程序即可。
我感觉,P-V交互是MVP模式运用的重点,本人水平有限,文中疏漏或讲解不到位的地方还请大家谅解。关于MVP,大家可以看看Artech和代震军的文章,你们会学到很多,还有一篇关于MVP的14条规则的文章也强烈推荐。
这篇文章是关于MVP运用的,下面我会介绍我是如何把WCF服务端加入进来的,以及我对三层结构运用的心得。
MVP+WCF+三层结构搭建项目框架(下)
转载于:https://www.cnblogs.com/yanchenglong/archive/2012/03/19/2403153.html
MVP+WCF+三层结构搭建项目框架(上)相关推荐
- [转]MVP+WCF+三层结构搭建项目框架
最近,我一直在重构之前做的一个项目,在这个过程中感慨万千.原先的项目是一个运用了WCF的C/S系统,在客户端运用了MVC模式,但MVC的View.Model耦合以及WCF端分布式欠佳等问题让我有了重构 ...
- 使用React和Tailwind CSS搭建项目框架
众所周知,Tailwind CSS框架越来越流行,所以我决定尝试学习并使用Tailwind CSS来搭建一个项目模板,一方面自己深入学习下,二来帮助新人更快地上手Tailwind CSS开发. 创建一 ...
- Extjs6.2.0搭建项目框架
排版什么的不是很会 1.安装 首先你总要去官网下载ext-6.2.0-gpl.zip和安装Sencha CMD工具来管理ExtJs项目,ext-6.2.0-gpl.zip下载完成解压先放在一边,一会用 ...
- 使用spring boot+shiro+jwt+mybatis-plus搭建项目框架
1.创建spring boot项目,并导入依赖 pom.xml <dependencies><dependency><groupId>org.springframe ...
- JavaWeb学习之路——SpringBoot搭建项目框架(一)
1.使用maven创建Spring Boot项目 1)在pom.xml中修改jdk版本 <!--jdk版本--> <properties> <java.version&g ...
- 新锐房地产销售管理系统 (部分流程)技术解析(一)用三层架构搭建项目
一.新建项目客户端(UIL) 第一步:打开Vs2010界面,点击左上角文件,点击新建,选择项目 第二步:点击Windows类型,选择Windows窗体应用程序,在名称中输入项目名称(解决方案名称),位 ...
- 搭建项目框架时需要考虑的几个问题
1.多渠道打包 2.强制更新功能 3.后台错误提示必须展示前台,不可自定义,以给用户进行正确的引导 4.涉及重要的数据要做加密处理 5.所引入的框架需考虑到可维护性,以免android系统出现重大更新 ...
- OA系统:搭建项目框架
一.创建facaioa项目 workspace编码统一更改为utf-8 找到Preferences 二.创建包和文件夹 1.创建包 2.创建WebRoot下文件夹 system:人事管理 duty:考 ...
- 使用VS搭建三层结构
参考资料:http://book.51cto.com/art/200906/131383.htm 通过一个简单的登录功能实现,讲述如何搭建三层结构. 1.搭建数据访问层 (1)打开VS 2005开发环 ...
最新文章
- linux下运行js挖矿,利用 JavaScript 代码挖矿
- php 文档在线查看器,Office Web Viewer 在线Office文档查看器API
- db2查询表结构语句_常用的sql语句集合(适合数据库初级人员)
- python数据可视化柱状图_python数据可视化示例柱状图
- react中dispatch_reactjs – TypeError:dispatch不是函数.在React无状态组件中
- 微信小程序多次跳转后不能点_京东小程序 Taro 开发对比原生开发测评
- 百炼-2701:与7无关的数
- MT4 缠论双线MACD面积背离指标
- 截图上传录屏gif上传工具推荐
- java工程师考华为证有用吗_华为初级认证网络工程师有什么用?大学生适合考吗?...
- 历届奥斯卡最佳影片及下载地址
- Endnote 导入enw文件无响应及解决方法
- 如何识别手写汉字?跟着步骤就能完成
- 推广网店的12个秘诀
- 访问网站报错‘您目前无法访问XXXX 因为此网站使用了 HSTS
- 压力测试Jmeter+badboy
- 为资产分类定义折旧范围_FI-AA配置逻辑
- wdcp3.2版本建站流程
- GTA5前置任务怎么使用差事传送
- 张国荣:去世前深受新欢旧爱拉锯战折磨(图