1. PV与SC
解决View难以测试最好的办法就是让他无须测试。如果View不需要测试,其先决条件就是让它尽可能不涉及UI处理逻辑,这就是PV模式的目的所在。
如果我们纯粹的采用PV模式来设计View意味着我们需要将View中的UI元素通过属性的形式暴露出来。具体来说,当我们为View定义接口的时候,需要定义基于UI元素的属性使Presenter可以对View进行细粒度操作,但这并不意味着我们直接将View上的控件暴露出来。
举个例子,假如我们开发的HR系统中有这样一个界面,界面上有一个包含所有部门列表的DropDownList,还有一个是显示员工列表的GridView,还有一个查询按钮,我们可以通过查询按钮来讲选择的部门的员工显示出来。
如果为该View定义一个接口IEmployeeView我们不能如下所示的代码将上述的两个控件直接以属性的形式显示出来。针对具体控件类型的数据绑定属于View的内部细节,不能体现在表示用于抽象View的接口中。
public interface IEmployeeView { <span style="white-space:pre"> </span>DropDownList Departments{ get; } <span style="white-space:pre"> </span>GridView<span style="white-space:pre"> </span> Employees { get;} }
正确的接口和实现该接口的View应该采用如下的定义方式:Presenter通过对属性Departments和Employees赋值来实现对相应DropDownList和GridView的数据绑定,同时通过属性SelectedDepartment得到用户选择的筛选部门。为了尽可能让接口只暴露必需的信息,我们还特意将对属性的读/写作了控制。
public interface IEmployeeView { <span style="white-space:pre"> </span>IEnumerable<String> Departments{ set;} <span style="white-space:pre"> </span>String SelectedDepartment{ get;} <span style="white-space:pre"> </span>IEnumerable<Employee> <span style="white-space:pre"> </span>Employees{ set;} } public partial class EmployeeView : page , IEmployeeView { <span style="white-space:pre"> </span>public IEnumerable<string> Departments <span style="white-space:pre"> </span>{ <span style="white-space:pre"> </span>Set <span style="white-space:pre"> </span>{ <span style="white-space:pre"> </span>this.DropDownListDepartments.DataSource = value; <span style="white-space:pre"> </span>this.DropDownListDepartments.DataBind(); <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>public string SelectedDepartment <span style="white-space:pre"> </span>{ <span style="white-space:pre"> </span>get{ <span style="white-space:pre"> </span>return this.DropDownListDepartments.SelectedValue; <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>public IEnumerable<Employee> Employees <span style="white-space:pre"> </span>{ <span style="white-space:pre"> </span>set <span style="white-space:pre"> </span>{ <span style="white-space:pre"> </span>this.GridViewEmployees.DataSource = value; <span style="white-space:pre"> </span>this.GridViewEmployees.DataBind(); <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>} }
PV模式将所有的UI处理逻辑全部定义在Presenter上,意味着所有的UI处理逻辑都可以被测试,从可测试性的角度来看这是一种不错的选择。但它要求将View中可供操作的UI元素定义在对应的接口中,对于一些复杂的富客户端(Rich Client)应用的View来说,接口成员的数量可能会变得很多。另外,由于Presenter需要在控件级别对View进行细粒度的控制,这往往会使原本简单的逻辑复杂化。这种情况下我们往往采用SC模式。
在SC模式下,为了降低Presenter的复杂度,我们倾向于将诸如数据绑定和显示数据格式化这样简单的UI处理逻辑转移到View中,这些处理逻辑会体现在View实现的接口中,尽管View从Presenter接管了部分UI处理逻辑,但Presenter依然是整个三角关系的驱动者,View被动的地位依然没有改变。对于用户作用在View上的交互操作,View本身并不进行响应,它只会将交互请求转发给Presenter,后者在独立完成相应的处理流程(可能涉及针对对Model的调用)之后会驱动View对用户交互请求进行响应。