Web Client Software Factory系列(3):View-Presenter模式

概述

将一个ASP.NET站点分离为多个独立的模块,一个最大的问题就是与页面相关联的大多数业务逻辑驻留在该页面的源代码文件中,我们几乎做不到将源代码文件分为多个独立的程序集。为了真正创建独立的与站点中的页面相关联的功能模块,所有页面逻辑、事件处理逻辑和导航逻辑需要以某种方式从页面提取出来并保存在独立的程序集中。

Web Composite应用程序块中的默认解决方案是使用 View-Presenter 模式将页面逻辑分成不同的用于响应由视图(网页)转发的任意事件的类(表示器)。表示器类完全在业务模块中实现,从而将应用程序逻辑至于网站外,接口是在定义由视图实现的方法的业务模块中定义的。这样,网页可在结束时将所有事件转发给表示器,无需任何实际的特定于应用程序的职责。这样还可使设计表示器的测试更为容易,无需实际涉及到前端网页。

添加View

还是接着我们在上一篇中的示例,已经创建了相关的业务模块和服务,这里添加一个视图:

TerryLee_WCSF12

这里我们创建一个显示商品详细信息的视图ProductDetail,创建完成后,在资源管理器中可以看到:

TerryLee_WCSF13

添加了新页面ProductDetail.aspx 以及相应的源代码文件添加到 /Products 目录。对于Products 业务模块项目,该方案将添加新的类 ProductDetailPresenter (表示器)和相应的 IProductDetail (视图)接口,该接口已经由 ProductDetail.aspx文件中的源代码类实现,同时源代码类文件还包含一个页面应向其转发事件的相应的 ProductDetailPresenter 类的属性声明:

public partial class Products_ProductDetail : System.Web.UI.Page, IProductDetail
{
private ProductDetailPresenter _presenter;
protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
this._presenter.OnViewInitialized();
}
this._presenter.OnViewLoaded();
}
[CreateNew]
public ProductDetailPresenter Presenter
{
set
{
this._presenter = value;
this._presenter.View = this;
}
}
}
由于我们要显示Product的名称和品牌,打开IProductDetail.cs文件,添加如下两个属性:
public interface IProductDetail
{
public string Name;
public string Brand;
}

然后我们在页面中实现这两个属性:

public partial class Products_ProductDetail : System.Web.UI.Page, IProductDetail
{
private ProductDetailPresenter _presenter;
protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
this._presenter.OnViewInitialized();
}
this._presenter.OnViewLoaded();
}
[CreateNew]
public ProductDetailPresenter Presenter
{
set
{
this._presenter = value;
this._presenter.View = this;
}
}
public string Name
{
set
{
this.lbl_Name.Text = value;
}
}
public string Brand
{
set
{
this.lbl_Brand.Text = value;
}
}
}

实现Presenter

从上面的代码中可以看到,页面的源代码文件中非常干净,没有任何与业务逻辑有关的东西,至于数据从哪儿来,该怎么进行显示,就交给Presenter。接下来就要实现Presenter。打开ProductDetailPresenter.cs文件,这里可以实现页面中的任何事件,其中OnViewLoaded、OnViewInitialized两个方法的区别在于对应我们在Page中的IsPostBack判断,View属性是定义在泛型的Presenter中。编写完成后代码如下:

public class ProductDetailPresenter : Presenter<IProductDetail>
{
private ProductsController _controller;
public ProductDetailPresenter([CreateNew] ProductsController controller)
{
_controller = controller;
}
public override void OnViewLoaded()
{
Product product = _controller.GetProductById("1");
View.Name = product.Name;
View.Brand = product.Brand;
}
public override void OnViewInitialized()
{
}
}

在浏览器中查看后,可以看到页面如下:

TerryLee_WCSF14

这里有个问题是页面的显示出来了,但是在左边的树形控件导航中并没有看到ProcuctDetail的链接。这是下面要说的模块站点映射。

模块站点映射

在解决方案母版页面中,包含一个绑定到 SiteMapDataSource 的树视图控件,用于显示站点上的可导航页面,另外还有SiteMapPath控件。这里的SiteMapDataSource并没有绑定到标准 Web.sitemap 文件,而是绑定到独立地从每个模块收集导航信息的自定义SiteMapProvider。该Provider在应用程序启动时显式询问每个模块的站点地图信息,默认情况下,此 SiteMapProvider 作为默认提供程序注册,并将由 SiteMapDataSource 用于所有导航控件。

要使用特定模块的站点地图信息填充 ModuleSiteMapProvider,需要覆写从 ModuleInitializer 派生的类中的 RegisterSiteMapInformation 方法。我们在创建业务模块时会覆写此方法,并会将业务模块下的Default.aspx 页面插入到 SiteMapNodes 集合,但是我们需要将其他任何页面添加到站点的同时也将其添加到集合中。

protected virtual void RegisterSiteMapInformation(ISiteMapBuilderService siteMapBuilderService)
{
SiteMapNodeInfo moduleNode = new SiteMapNodeInfo("Products", "~/Products/Default.aspx", "Products");
siteMapBuilderService.AddNode(moduleNode);
SiteMapNodeInfo productDetailNode = new SiteMapNodeInfo("ProductDetail","~/Products/ProductDetail.aspx","ProductDetail");
siteMapBuilderService.AddNode(productDetailNode,moduleNode);
}
SiteMapNodeInfo有三个参数,分别对应为键值、页面地址、显示标题。不过这里又添加了硬编码,可以根据自己的需要修改,使其能够从配置文件中读取站点映射信息。
现在再运行程序,可以看到ProductDetail页面已经添加在了Products业务模块下,并且页面上已经添加了导航栏。
TerryLee_WCSF15 

结束语

利用Web Client Software Factory,一个简单的Composite Web应用程序块和View-Presenter模式结合的使用就到这里了。下篇我们做更接近实际的例子,如何使用View-Presenter模式进行数据绑定和ObjectContainerDataSource控件的使用。
示例代码下载:/Files/Terrylee/WebClientDemo1.rar

作者:TerryLee
出处:http://terrylee.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
posted @ 2007-11-27 20:33 TerryLee 阅读(4147) 评论(27)  编辑 收藏 网摘 所属分类: [10]  模式与实践

  回复  引用  查看    
#1楼 2007-11-27 20:43 | 老夫子系      
好,TerryLee兄,速度好快呀!
  回复  引用  查看    
#2楼 [楼主]2007-11-27 20:46 | TerryLee      
@老夫子系
好久没更新,回来“赎罪”了:)
  回复  引用  查看    
#3楼 2007-11-27 21:34 | 暗香浮动      
高速。
  回复  引用  查看    
#4楼 2007-11-27 22:12 | Jeffrey Zhao      
这个还是简单的例子,期待下一片!
// 不过代码缩进怎么都消失了……
  回复  引用  查看    
#5楼 [楼主]2007-11-27 22:36 | TerryLee      
@暗香浮动
:)
  回复  引用  查看    
#6楼 [楼主]2007-11-27 22:37 | TerryLee      
@Jeffrey Zhao
嗯,我再想是不是用简单的例子,比较容易说明白一点,否则代码会太多:)

// 我用Writer写的,其它几篇就没这个问题,奇怪了

// Update:前面几篇也有问题了-_-
  回复  引用  查看    
#7楼 2007-11-27 22:50 | Jeffrey Zhao      
@TerryLee
只是简单的例子不说明问题啊。
我就是有MVP的经验,所以觉得这些东西看上去很美,但是用上去很麻烦很土啊。
  回复  引用  查看    
#8楼 [楼主]2007-11-27 22:56 | TerryLee      
@Jeffrey Zhao
嗯,争取后面找点复杂的例子:)
  回复  引用  查看    
#9楼 2007-11-27 23:17 | 阿不      
是啊,有时候MVP用着心里没底喔。
有时候大家都担心是不是误用了。
可能还是得总结出一些基本原则出来
  回复  引用  查看    
#11楼 2007-11-28 08:58 | Clingingboy      
如果数据类型为DateTime,且View属性未赋值,以下就会出错
public DateTime Brand
{
set
{
this.lbl_Brand.Text = value;
}
}
}

个人感觉这个适合webform,这里绑的比较死,不知道大家如何理解?
  回复  引用  查看    
#12楼 [楼主]2007-11-28 09:22 | TerryLee      
@阿不
MVP用起来是有点麻烦,有时候开发人员会在View、Presenter之间转晕:)

可能好的一点就是便于UI的测试

  回复  引用  查看    
#13楼 [楼主]2007-11-28 09:29 | TerryLee      
@volnet(可以叫我大V)
CodeProject这篇文章也不错
http://www.codeproject.com/aspnet/ModelViewPresenter.asp
  回复  引用  查看    
#14楼 2007-11-28 09:44 | Clark Zheng      
不会转晕,强制性的剥离,采用presenter接口方式与view交互,谁用谁知道
  回复  引用    
#15楼 2007-11-28 11:21 | 婚纱 [未注册用户]
支持,受益很深啊.
  回复  引用    
#16楼 2007-11-28 11:26 | CW [未注册用户]
TO: TerryLee

向大哥请教一下, 到底现在是学MVC还是学MVP好呢? 有些晕。。。

MVC与MVP到底哪个是未来的发展方向?

初学有些迷茫。。。。

谢谢。。。
  回复  引用  查看    
#17楼 2007-11-28 13:02 | 紫色阴影      
@TerryLee
live write有一个plug-in叫paste from visual studio还不错
  回复  引用  查看    
#18楼 [楼主]2007-11-28 20:30 | TerryLee      
@CW
MVP是从MVC演变过来的

应该都要有所了解
  回复  引用  查看    
#19楼 [楼主]2007-11-28 20:30 | TerryLee      
@紫色阴影
谢谢
  回复  引用  查看    
#20楼 2007-11-29 09:12 | Enzo      
@TerryLee
“赎罪” 用的太言重了
呵呵

  回复  引用  查看    
#21楼 [楼主]2007-11-29 20:11 | TerryLee      
@ Enzo
:)
  回复  引用  查看    
#22楼 2007-12-04 14:22 | 赤脚小子      
TerryLee兄
多谢了,正准备好好研究下WCSF,就看到了你的这个系列。
这篇里有个疑问,文中:
public interface IProductDetail
{
public string Name;
public string Brand;
}
似乎应该为:
public interface IProductDetail
{
string Name { set;}
string Brand { set;}
}
才对。
  回复  引用  查看    
#23楼 2008-03-19 21:59 | wuhang      
--引用--------------------------------------------------
赤脚小子: TerryLee兄
多谢了,正准备好好研究下WCSF,就看到了你的这个系列。
这篇里有个疑问,文中:
public interface IProductDetail
{
public string Name;
public string Brand;
}
似乎应该为:
public interface IProductDetail
{
string Name { set;}
string Brand { set;}
}
才对。
--------------------------------------------------------
同意楼上的看法,的确是这样设置才对!在VS2008里面测试通过了!
  回复  引用    
#24楼 2008-11-14 10:13 | 你的追随者 [未注册用户]
你的文章真是太棒了,我感觉从代码讲解,到设计模式,框架,wcf,silverlight都能看到你的相关文章,并且讲很透彻并且无私奉献你的源码,你的文章 的更新速度还很快,你真是强人,向你学习!
  回复  引用  查看    
#25楼 [楼主]2008-11-15 00:36 | TerryLee      
@你的追随者
呵呵,谢谢鼓励:)

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2008-10-28 12:17 编辑过
Google站内搜索



相关文章:

相关链接: