1.2 MVC 变体
MVC 是一种Pattern 另外一种说法是ParaDigm 范例
模式和范例的区别在于前者可以应用到具体的应用上,而后者则仅仅提供一些指导方针
1.2.1 MVP
Model View Presenter 交互图
MVC 模式中元素之间 “混乱”的交互主要体现在允许View 和 Model 绕开Controller进行单独交流,这在MVP 模式中得到了充分解决
PV 模式 (passive View)
解决View很难测试的最好方法是让他无需测试,让UI处理更少逻辑,被动的交给Presenter来处理
暴漏UI不一定必须暴漏UI控件,
没有必要写成
public interface IemployeeSearchView1 { DropDownList Department { get; } GridView Employees { get; } }
而可以通过数据的属性来定义接口,这样就可以将presenter中的UI处理逻辑分离出来
public interface IemployeeSearchView { IEnumerable<string> Departments { set; } string SelectedDedpartment { get; } IEnumerable<Employee> Employees { set; } }
具体的实现方法如下:
public partial class _Default : System.Web.UI.Page, IemployeeSearchView { protected void Page_Load(object sender, EventArgs e) { } public IEnumerable<string> Departments { set { this.DropDownListDepartments.DataSource = value; this.DropDownListDepartments.DataBind(); } } public string SelectedDedpartment { get { return this.DropDownListDepartments.SelectedValue; } } public IEnumerable<Employee> Employees { set { this.GridViewEmployees.DataSource = value; this.GridViewEmployees.DataBind(); } } }
这样做意味着所有的UI逻辑都可以被测试,但是它要求将所有可供操作的UI元素定义在对应的接口中,无疑会增加Presenter 的复杂度,这就引出SC模式
SC模式(supervsing Controller)
View 和Presenter 之间的交互式整个MVP的核心,能否正确地应用MVP模式来架构应用,主要取决于View 和Presenter 之间的关系 在由model、View Presenter 组成的三角关系中,核心不是View 而是Presenter,Presenter 不仅是View调用的中介,而是最终决定如何响应用户交互行为的决策者。
View 本身仅仅实现单纯的独立的UI处理逻辑,他处理的数据应该是Presneter实时推送过来的,所以View 尽可能不去维护数据状态,定义在IView的接口最好只包含方法,而避免属性的定义,Presenter 所需的关于View的状态应该在接收到View发送的用户请求时一次性推送,而不需要通过View 的属性去获得它
以下为所有代码
Model
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; } }
EmployeeRespository
public class EmployeeRepository { private static IList<Employee> employees; static EmployeeRepository() { employees = new List<Employee>(); employees.Add(new Employee("001", "张三", "男", new DateTime(1981, 8, 1), "销售部")); employees.Add(new Employee("001", "李四", "女", new DateTime(1982, 8, 4), "人事部")); employees.Add(new Employee("001", "王五", "男", new DateTime(1981, 9, 1), "人事部")); } public IEnumerable<Employee> GetEmployees(string department = "") { if (string.IsNullOrEmpty(department)) { return employees; } return employees.Where(e => e.Department == department).ToArray(); } }
IEmployeeSearchView
public interface IEmployeeSearchView { void BindEmployees(IEnumerable<Employee> employees); void BindDepartments(IEnumerable<string> departments); } public class DepartMentSelectedEventArgs : EventArgs { public string Department { get; private set; } public DepartMentSelectedEventArgs(string department) { this.Department = department; } }
EmployeeSearchPresenter
public class EmployeeSearchPresenter { public IEmployeeSearchView View { get; private set; } public EmployeeRepository Repository { get; private set; } public EmployeeSearchPresenter(IEmployeeSearchView view) { this.View = view; this.Repository = new EmployeeRepository(); view.DepartmentSelected += view_DepartmentSelected; } public void Initialize() { var employees = this.Repository.GetEmployees(); this.View.BindEmployees(employees); string[] departments = new string[] { "销售部", "人事部", "IT部" }; this.View.BindDepartments(departments); } void view_DepartmentSelected(object sender, DepartMentSelectedEventArgs e) { string department = e.Department; var employees = Repository.GetEmployees(department); this.View.BindEmployees(employees); } }
Web
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="Chaper1SC.WebForm1" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title></title> </head> <body> <form id="form1" runat="server"> <div id="page"> <div style="text-align: center; clear: both"> 请选择部门:<asp:DropDownList ID="dpDownListDepartments" runat="server"></asp:DropDownList><asp:Button Text="查询" runat="server" OnClick="Unnamed_Click" /> </div> <div> <asp:GridView ID="GvEmployees" runat="server" AutoGenerateColumns="false" Width="100%"> <Columns> <asp:BoundField DataField="Name" HeaderText="姓名" /> <asp:BoundField DataField="Gender" HeaderText="性别" /> <asp:BoundField DataField="BirthDate" HeaderText="出生日期" DataFormatString="{0:dd/MM/yy}" /> <asp:BoundField DataField="Gender" HeaderText="性别" /> </Columns> </asp:GridView> </div> </div> </form> </body> </html>
public partial class WebForm1 : System.Web.UI.Page, IEmployeeSearchView
{
public event EventHandler<DepartMentSelectedEventArgs> DepartmentSelected;
public EmployeeSearchPresenter Presenter { get; private set; }
public WebForm1()
{
this.Presenter = new EmployeeSearchPresenter(this);
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
this.Presenter.Initialize();
}
}
public void BindEmployees(IEnumerable<Employee> employees)
{
this.GvEmployees.DataSource = employees;
this.GvEmployees.DataBind();
}
public void BindDepartments(IEnumerable<string> departments)
{
this.dpDownListDepartments.DataSource = departments;
this.dpDownListDepartments.DataBind();
}
protected void Unnamed_Click(object sender, EventArgs e)
{
string deparment = this.dpDownListDepartments.SelectedValue;
DepartMentSelectedEventArgs args = new DepartMentSelectedEventArgs(deparment);
if (null != DepartmentSelected)
{
DepartmentSelected(sender, args);
}
}
}