MVP模式在Web和Windows应用程序中的应用

MVP(Model-View-Presenter)模式是一种设计模式,广泛应用于Web和Windows应用程序中。它特别适用于需要用户界面交互的应用,如Windows应用程序中的WinForms和Web应用中的WebForms。MVP模式的主要目标是实现关注点分离或松耦合整个系统,以便可以测试应用程序的每个部分。

使用代码实现MVP模式

为了示例,将使用Northwind数据库。通常,使用LINQ来访问数据库,因此为整个Northwind数据库创建了一个dbml。在这里,可以将数据层表示为模型。模型代表数据层,数据层可以是传统的类来访问数据库或一些服务来访问数据库。在这里,将使用LINQ类作为数据层。

为了简化说明,Web界面非常简单。它将加载所有类别,并在选择类别时显示所选类别下的产品列表。它应该有一个下拉列表来保存所有类别。当用户从类别列表中选择类别时,一个GridView将显示所选类别下的所有产品。显然,有两个事件:一个是PageLoad,它将在类别列表中显示所有类别;另一个是DropDownListBox的SelectedIndexChanged,它将在GridView中为所选类别填充产品。此外,还应该有一些列表/集合类来保存类别/产品的列表。

对于视图,将创建接口和委托。以下是代码示例:

public delegate void CategoryChangeEventHandler(object source, CategoryEventArgs e); public interface IProductListing { IEnumerable<Category> Categories { get; set; } IEnumerable<Product> Products { get; set; } event EventHandler ListingCategory; event CategoryChangeEventHandler ChangeCategory; }

代码非常简单。声明了两个枚举接口来保存类别/产品的列表和两个事件;一个是类别列表,另一个是类别更改。在ChangeCategory事件中,声明了一个自定义的EventArgs类来保存所选类别ID。以下是CategoryEventArgs的代码:

public class CategoryEventArgs : EventArgs { private Int16 _CategoryID; public CategoryEventArgs(Int16 CategoryID) { this._CategoryID = CategoryID; } public Int16 CategoryID { get { return this._CategoryID; } set { this._CategoryID = value; } } }

Presenter类负责在视图触发事件时执行操作,并且Presenter将响应事件。要获取视图实例,通过Presenter的构造函数传递视图。这种方法称为构造函数注入——一种依赖注入方法。然后,将视图事件与Presenter中声明的方法订阅。以下是代码示例:

public class ProductListingPresenter { IProductListing _view; public ProductListingPresenter(IProductListing view) { this._view = view; this._view.ListingCategory += this.OnCategoryListing; this._view.ChangeCategory += this.OnCategoryChanged; } private void OnCategoryListing(object source, EventArgs e) { NorthwindDataContext oDataContext = new NorthwindDataContext(); this._view.Categories = from category in oDataContext.Categories select category; } private void OnCategoryChanged(object source, CategoryEventArgs e) { NorthwindDataContext oDataContext = new NorthwindDataContext(); this._view.Products = from product in oDataContext.Products where product.CategoryID == e.CategoryID select product; } }

在这里,可以看到视图是通过Presenter使用构造函数注入传递的。现在订阅视图的ListingCategory事件与Presenter的OnCategoryListing方法。同样,订阅视图的ChangeCategory事件与Presenter的OnCategoryChanged方法。

单元测试

在开始时,提到MVP模式确实使单元测试变得更容易。在这个例子中,不需要编写aspx页面来测试应用程序。将编写一个模拟类来测试应用程序的功能,然后将代码放入aspx页面。以下是模拟类的代码:

public class MockProductListing : IProductListing { IEnumerable<Category> _categories; IEnumerable<Product> _products; public void TestPageLoad() { EventHandler handler = this.ListingCategory; if (handler != null) { handler(this, new EventArgs()); } } public void TestProductListingByCategory() { CategoryChangeEventHandler handler = this.ChangeCategory; if (handler != null) { CategoryEventArgs oArgs = new CategoryEventArgs(Convert.ToInt16(1)); handler(this, oArgs); } } #region IProductListing Members public IEnumerable<Category> Categories { get { return this._categories; } set { this._categories = value; } } public IEnumerable<Product> Products { get { return this._products; } set { this._products = value; } } public event EventHandler ListingCategory; public event CategoryChangeEventHandler ChangeCategory; #endregion }

在模拟类中,需要实现视图接口。将测试两个事件;一个是PageLoad;另一个是类别下拉列表的SelectedIndexChanged。可以看到,写了两个测试方法来测试这两个事件。

以下是测试方法的代码:

[TestMethod] public void PageLoad() { List<Category> categories = new List<Category>(); MockProductListing _view = new MockProductListing(); ProductListingPresenter _presenter = new ProductListingPresenter(_view); _view.TestPageLoad(); foreach (Category cat in _view.Categories) { categories.Add(cat); } Assert.AreEqual(8, categories.Count); } [TestMethod] public void TestProductsByCategory() { List<Product> products = new List<Product>(); MockProductListing _view = new MockProductListing(); ProductListingPresenter _presenter = new ProductListingPresenter(_view); _view.TestProductListingByCategory(); foreach (Product prod in _view.Products) { products.Add(prod); } Assert.AreEqual(12, products.Count); }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485