监控控制器
将 UI 分解为视图和控制器,其中视图处理与底层模型的简单映射,而控制器处理输入响应和复杂的视图逻辑。
2006 年 6 月 19 日
这是我在 2000 年代中期进行的 进一步的企业应用程序架构开发 写作的一部分。不幸的是,太多其他事情吸引了我的注意力,因此我没有时间进一步研究它们,而且在可预见的未来我也没有看到太多时间。因此,这些材料处于草稿阶段,在我能够找到时间再次处理它们之前,我不会进行任何更正或更新。
许多 UI 框架提供了轻松地在视图和模型之间进行映射的能力,通常使用某种形式的 数据绑定。这些方法在允许您声明性地设置视图和模型中元素之间的关系方面非常有效。但是,通常情况下,存在更复杂的关系,需要您拥有更复杂的视图逻辑。这种逻辑可能难以管理,尤其是在嵌入视图中时难以测试。
监控控制器 使用控制器来处理输入响应,也用于操作视图以处理更复杂的视图逻辑。它将简单的视图行为留给声明性系统,仅在需要超出声明性所能实现的效果时才进行干预。
工作原理
监控控制器 将表示功能分解为两个部分:控制器(通常称为演示者)和视图。需要显示的域数据是独立的,遵循粗略的 MVC 术语,我将称之为模型,尽管它不必是 域模型。职责的基本划分与模型-视图-演示者架构在它的 Dolphin 形式中相呼应,如 Bower 和 McGlashan 所述。
一个 监控控制器 具有两个主要职责:输入响应和部分视图/模型同步。
对于输入响应,控制器以演示者风格运行。用户手势最初由屏幕小部件处理,但是它们在响应中所做的只是将这些事件传递给演示者,演示者处理所有后续逻辑。
对于视图/模型同步,控制器尽可能地将此工作委托给视图。视图通常使用某种形式的 数据绑定 来填充其字段的大部分信息。如果 数据绑定 不足以满足更复杂的交互,则控制器会介入。
图 1:评估示例的类图。
图 2:显示输入低实际值的响应的时序图。
在评估窗口中,初始文本更改由视图中的文本字段小部件处理。该小部件由控制器观察,因此当文本发生更改时,小部件会发出一个事件,导致控制器的 actualFieldChanged
方法被调用。然后,该方法处理对事件的完整响应。它首先更新读取模型对象的实际值。窗口观察读取对象,因此对读取值的更改会触发刷新。将实际和方差文本字段的文本映射到读取的相应属性非常容易,从而更新这些值。为了我们的示例目的,更改颜色会更复杂一些。读取对象可以(也应该)确定读取应该属于哪个类别。一个复杂的小部件可能能够将它的文本颜色绑定到这样的类别,但我们假设我们没有这样聪明的东西。在这种情况下,控制器会接管直接设置方差字段的文本颜色。
如示例所示,一个好的 监控控制器 的本质是尽可能少做。让视图尽可能多地处理,只有在涉及更复杂的逻辑时才介入。
使用 监控控制器 的主要原因之一是可测试性。假设视图难以测试,通过将任何复杂逻辑移到控制器中,我们将逻辑放在更容易测试的地方。但是,为了对控制器运行测试,我们确实需要某种形式的视图,因此 测试替身 通常是必要的。有了替身,我们就不需要任何 UI 框架对象来测试 UI 行为中更尴尬的部分。
此可测试性问题会影响另一个决定 - 控制器是否应该直接访问视图及其小部件,还是通过中介访问。使用中介,我们为控制器构建了一个 网关。网关定义了控制器操作的接口。然后,一个实现会适应窗口的接口,而另一个实现会提供一个用于测试的存根(您也可以使用模拟)。这与您需要 被动视图 所采用的方法相同。
图 3:在控制器和窗口之间使用中介。
到目前为止的讨论建议将 流同步 与 监控控制器 一起使用,但这并非必须。可以使用 观察者同步,但需要对其进行修改,以便控制器观察模型而不是视图。
何时使用它
考虑使用 监控控制器 的两个主要原因是:分离 自主视图 的复杂性,以及提高可测试性。
分离的优势在于它将所有行为复杂性从基本窗口本身中分离出来,使其更容易理解。这种优势被控制器仍然与它的屏幕紧密耦合的事实所抵消,它需要对屏幕的细节有相当深入的了解。在这种情况下,是否值得付出将它变成一个独立对象的努力是一个真正的问号。
可测试性原因是更令人信服的原因。我与之交谈过的许多人发现,使用某种形式的控制器对创建可正确测试的 UI 产生了很大影响。
如果可测试性是您的驱动力,那么驱动问题是将多少行为保留在视图中。 被动视图 与 监控控制器 非常相似,但不同之处在于 被动视图 将所有视图更新行为都放在控制器中,包括简单的情况。这会导致额外的编程,但确实意味着所有表示行为都是可测试的。两者之间的选择取决于您拥有哪种 数据绑定 支持,以及您是否愿意通过控制器测试将这些支持留待测试。
第三种选择是 演示模型。这同样将大多数行为从视图中分离出来,但留给视图同步所有更新。 演示模型 和 监控控制器 之间的测试覆盖范围没有太大区别 - 很大程度上(与 被动视图 一样)取决于个人判断。