文/李光磊
我们所开发的应用程序大多都需要提供一个图形用户界面(GUI)。关于GUI应用的架构设计, 已经有了很多模式, 比如Martin Fowler的blog中有一篇"GUI Architectures“, 里面介绍了Form & Control、MVC、MVP、Passive View、Presentation Model、 Supervising Controller、Event Aggregator, Observer Synchronization等多种模式。模式可以帮助我们建立优雅的架构, 但前提是弄清楚模式的应用场景。这些模式自然不是凭空产生的, 都是为了解决具体的问题. 模式在实现上的差别, 通常都体现了在约束间的不同取舍, 以及问题的差别. 弄清楚GUI应用面临的设计上的问题, 有助于我们正确的挑选设计方案. 下面我们来看一些GUI应用常见的设计问题。
第一个问题就是界面的变化和业务的变化频率不同, 通常是界面变化更频繁, 而我们希望一方的变化不至于影响另一方的逻辑. 对于这个问题, 一个自然的解决方案就是分离界面显示逻辑和后台业务逻辑. MVC和MVP都涉及到了这一点, 它们的共同特点就是把View和应用程序的其它部分分开了. 这是一个关键的分离, 从此之后应用被分为两部分, 抛开它们彼此可以独立的变化不说. 最大的好处是这两部分的问题也可以分而治之。应用程序的其它部分有自己的问题和方案, 不在我们讨论范围内. 我们后面将聚焦在View和相关的显示逻辑方面的问题. 当然这种分离也不是没有代价的, 一个立即的问题就是View如何更新. MVC和MVP把View分出来制造了这个问题, 它们也同时提供了手段解决这个问题。
对视图更新的处理是MVC和MVP在实现上的主要区别: MVP中View不需要知道Model, Presenter直接操作View。MVC中View知道Model, 自己根据Model来更新自己的状态。
(图片来自: http://msdn.microsoft.com/en-us/library/ff647859.aspx)
跟View相关的另一个常见问题就是可测试性. 即使其它分出去的部分可以独立测试, 但剩下来的View依然Hold住了一部分显示相关的逻辑. 显示逻辑也是逻辑, 也需要测试, 而通常直接测试GUI界面是相对难以测试的. 现有直接测试GUI的测试工具都面临以下问题:
一个思路就是把显示逻辑从View中分离, 让View退化为简单的GUI控件的容器. MVP做出了最初的努力, 而另外两个模式更加强调了这一点: Presentation Model 和Passive View。
我们上面讨论的都是相对简单的GUI, 比如我们其实假定了View和Model的一一对应, 甚至也假定了应用只有一个View。 然而我们还有多视图的情况。 多视图带来了以下问题:
Martin Fowler blog中描述的Flow Synchronization 和 Observer Synchronization 为当Model变化时刷新多个视图提供了两种方式, 分别应对不同的情况。
传统上还有一种用于解决交互的可控性并让View之间彼此解耦的模式, 就是Mediator。 当我们在应用Flow Synchronization时, 如果把View之间的交互都抽取到一个中介者对象里面, 每个View都不知道其它View, 只知道中介者对象, 当有事件发生时, 由中介者对象来更新Model和其它View, 则我们可以获得相对清晰的交互和相对松散的耦合。 来看一下<<设计模式>>里面对Mediator的描述:
意图:
适用性:
效果:
多视图的另一个问题就是事件的循环触发问题。 场景如下:事件A发生->事件A处理函数->处理过程中触发了事件B->事件B处理函数->处理过程中又触发了事件A->……一个简单的例子比如界面上有两个文本框, 要保证它们的和一直都是100。 比如文本框A输入30的时候, 文本框B要显示70。 文本框B输入40的时候, 文本框A要显示60。 我们在处理第一个输入事件的时候需要设置第二个文本框的值, 而这个设值动作会触发第二个文本框的事件处理, 它也要设置第一个文本框的值……如此循环。
通常的处理方式有几种, 目的都相同:尽量减少不必要的事件发送
谈到事件的粒度, 过细的粒度会引起另外一个问题:注册事件处理函数太繁琐, 不易看清交互。 Event Aggregator可以来解决这个问题。
最后回过头来看一下已有的几个模式各自的重点 :
MVC | MVP | Presentation Model | Passive View | Flow Synchronization | Observer Synchronization | Mediator | |
---|---|---|---|---|---|---|---|
显示逻辑与业务逻辑的分离 | Yes | Yes | Yes | ||||
视图的更新 | Yes | Yes | Yes | ||||
视图与数据的分离(可测试性) | Yes | Yes | Yes | ||||
多视图间的一致性 | Yes | Yes | |||||
交互的可控性 | Yes |
其中:
更全面的比较, 请参见老马的"GUI Architectures“, 及里面的链接。
作者介绍:
李光磊
ThoughtWorks资深咨询师