目前做这个产品也有一段时间了,其中MVP模式用的非常的频繁,但是很多时候没有用对,MVP模通常是这样
Model 层, IVeiw 视图接口层, View视图层, Presenter层
Model层存着业务逻辑和数据处理, Presenter 则裁决如何处理用户的请求,IView 则是把Presenter和Veiw 之间进行解耦,使Presenter可以不依赖于具体的视图, View 通过Presenter 对Model 进行访问,这是MVP与MVC模式的主要区别,在MVC中,Model可以跨过Controller直接与View交互.
下面看项目中一个错误使用案例,一个视图接口定义
public interface ITargetWizardView : ISmartPartInfo
{
UltraTree GetNavigateTargetTree();
}
上面的方法返回了一个具体的控件类型,而这个在Presenter里面需要调用的方法应该是与控件类型无关的(与控件类型产生了耦合,试想如果控件换成了另一种控件,这个方法还能用吗?),并且不应该有返回值
在<<ASP.NET MVC 4 框架揭秘>>中有这样一段话:
对于绑定到View上的数据,不应该是View从Presenter上"拉"回来的,应该是Presenter主动推给View.从消息流(或者消息交换模式)的角度来讲,不论是View向Presenter完成针对用户交互请求的通知,还是Presenter在进行交互请求处理过程中驱动View完成相应的UI操作,都是单向(One-Way)的.反应在应用程序接口上就意味着不论是定义在Presenter中被View调用方法,还是定义在IView接口中被Presenter调用的方法最好都没有返回值.如果不采用方法调用的形式,我们也可以通过事件注册的方式实现View和Presenter的交互,事件机制体现的消息流无疑是单向的.
下面看一个正确使用的例子.
有一个员工基本信息类
public class Employee
{
public string Id
{
get;
private set;
}
public string Name
{
get;
private set;
}
public string Gender
{
get;
private set;
}
public DateTime BirthDate
{
get;
private set;
}
public string Department
{
get;
private set;
}
public Employee(string id, string name, string gender,
DateTime birthDate, string department)
{
this.Id = id;
this.Name = name;
this.Gender = gender;
this.BirthDate = birthDate;
this.Department = department;
}
}
下面这个类作为操作这个对象的Model
public class EmployeeRepository
{
private static IList<Employee> employees;
static EmployeeRepository()
{
employees = new List<Employee>();
employees.Add(new Employee("001", "张三", "男", new DateTime(1981, 8, 24), "销售部"));
employees.Add(new Employee("001", "李四", "女", new DateTime(1982, 7, 10), "人事部"));
employees.Add(new Employee("001", "王五", "男", new DateTime(1981, 9, 21), "人事部"));
}
public IEnumerable<Employee> GetEmployees(string department = "")
{
if(string.IsNullOrEmpty(department))
{
return employees;
}
return employees.Where(e => e.Department == department).ToArray();
}
}
下面定义一个视图的接口,接口定义了BindEmployees和BindDepartments两个方法,分别用于绑定基于部门列表的DropDownList和基于员工的列表的GridView.除些之外,还定义了一个事件DepartmentSelected,该事件会在用户选择筛选部门后点击"查询"按钮时触发,事件类型为自定义DepartmentSelectedEventArgs
public interface IEmployeeSearchView
{
void BindEmployees(IEnumerable<Employee> employees);
void BindDepartments(IEnumerable<string> departments);
event EventHandle<DepartmentSelectedEventArgs> DepartmentSelected;
}
public class DepartmentSelectedEventArgs : EventArgs
{
public string Department
{
get;
private set;
}
public DepartmentSelectedEventArgs(string department)
{
this.Department = department;
}
}
下面是作为MVP核心的Presenter类.IView接口表现为它的一个只读属性,Model也作为它的一个只读属性,两者都在它的构造函数中进行初始化
public class EmployeeSearchPresenter
{
public IemployeeSearchView View
{
get;
private set;
}
public EmployeeRepository Repository
{
get;
private set;
}
public EmployeeSearchPresenter(IEmploySearchView view)
{
this.View = view;
this.Repository = new EmployeeRepository();
this.View.DepartmentSelected += OnDepartmentSelected;
}
public void Initialize()
{
IEnumerable<Employee> employees = this.Repository.GetEmployees();
this.View.BindEmployees(employees);
string[] departments = new string[] {"销售部", "采购部", "人事部", "IT部"};
this.View.BindDepartments(departments);
}
protected void OnDepartmentSelected(object sender, DepartmentSelectedEventArgs args)
{
string department = args.Department;
var employees = this.Repository.GetEmployees(department);
this.View.BindEmployees(employees)
}
}
下面类为一个Web页面类,它同时实现了视图接口IEmploySearchView
public partial class Default : Page, IEmployeeSearchView
{
public EmployeeSearchPresenter Presenter
{
get;
private set;
}
public event EventHandle<DepartmentSelectedEventArgs> DepartmentSelected;
public Default()
{
this.Presenter = new EmploySearchPresenter(this);
}
protected void Page_Load(object sender, EventArgs e)
{
if(!this.IsPostBack)
{
this.Presenter.Initialize();
}
}
protected void ButtonSearch_Click(object sender, EventArgs e)
{
string department = this.DropDownListDepartments.SelectedValue;
DepartmentSelectedEventArgs eventArgs = new DepartmentSelectedEventArgs(department);
if(null != DepartmentSelected)
{
DepartmentSelected(this, eventArgs);
}
}
public void BindEmployees(IEnumerable<Employee> employees)
{
this.GridViewEmployees.DataSource = employees;
this.GridViewEmployees.DataBind();
}
public void BindDepartments(IEnumerable<string> departments)
{
this.DropDownListDepartments.DataSource = departments;
this.DropDownListDepartments.DataBind();
}
}