第二十三章 展示器(PRESENTERS)和谦卑对象(HUMBLE OBJECTS)
在第22章中,我们介绍了展示器的概念。展示器是谦卑对象模式的一种形式,这有助于我们识别和保护架构边界。实际上,上一章的“整洁的架构”中充满了“谦卑对象”的实现。
谦卑对象模式
谦卑对象模式1是一种设计模式,最初被认为是一种帮助单元测试人员将易于测试的行为和难以测试的行为分开的方法。这个想法很简单:将行为分解成两个模块或类。其中一个模块是谦卑的;它包含了所有难以测试的行为,这些行为也是最基本的东西。另一个模块包含所有可测试的行为,这些行为是剥离出谦卑的对象的行为。
例如,图形用户界面很难进行单元测试,因为编写可以看到屏幕的测试是非常困难的,并且检查在那里显示的适当元素。但是,GUI的大部分行为实际上很容易测试。使用Humble Object模式,我们可以将这两种行为分为两个不同的类,分别称为展示器和视图。
展示器(PRESENTERS)和视图(VIEWS)
视图是难以测试的谦卑对象。此对象中的代码尽可能简单。它将数据移动到GUI中,但不处理该数据。
展示器是可测试的对象。它的工作是接受来自应用程序的数据并对其进行格式化,以便视图可以简单地将其移动到屏幕上。例如,如果应用程序想要在字段中显示日期,则会向展示器提交一个Date对象。然后展示器会将这些数据格式化为一个合适的字符串,并将其放置在一个称为视图模型(View Model)的简单数据结构中,视图可以从该数据结构中获取到。
如果应用程序想要在屏幕上显示金额,则可能会将Currency对象传递给演示者。展示器将使用适当的小数位和货币标记对该对象进行格式化,从而创建一个可放置在视图模型中的字符串。如果货币值为负值,则该值应该变为红色,那么视图模型中的简单布尔标志将被适当地设置。
屏幕上的每个按钮都有一个名称。这个名字将是展示器在视图模型中的一个字符串。如果这些按钮应灰显,展示器将在视图模型中设置适当的布尔标志。每个菜单项名称都是视图模型中的一个字符串,由展示器加载。展示器将每个单选按钮,复选框和文本字段的名称加载到视图模型中的相应字符串和布尔值中。应该在屏幕上显示的数字表由展示器加载到视图模型中格式正确的字符串表中。
屏幕上出现的任何事物,应用程序都有某种控制权,在视图模型中用字符串,布尔值或枚举表示。视图除了将视图模型中的数据加载到屏幕之外,没有什么可做的。因此,视图是谦卑的。
测试和架构
早就知道可测试性是良好体系结构的一个属性。谦卑对象模式就是一个很好的例子,因为将行为分为可测试和不可测试的部分通常定义了一个架构边界。展示器/视图边界是这些边界之一,但还有其他许多边界。
数据库网关
用例交互者和数据库之间是数据库网关。2这些网关是多态接口,包含数据库上应用程序可以执行的每个创建,读取,更新或删除操作的方法。例如,如果应用程序需要知道所有昨天登录的用户的姓氏,那么UserGateway接口将有一个名为getLastNamesOfUsersWhoLoggedInAfter的方法,该方法将Date作为其参数,并返回姓氏列表。
回想一下,我们不允许在用例层出现SQL;相反,我们使用具有适当方法的网关接口。这些网关是通过数据库层的类实现的。这个实现是一个谦卑对象。它只是简单地使用SQL,或者任何与数据库的接口,来访问每个方法所需的数据。相反,交互器并不谦虚,因为它们封装了特定于应用程序的业务规则。虽然他们不谦虚,但这些交互者是可以测试的,因为网关可以被替换为适当的预设数据和测试替换3。
数据映射器
回到数据库的话题,你认为ORM(对象关系映射,比如Hibernate框架)属于哪一层?首先,让我们弄清楚一些事情:没有像ORM那样的东西。原因很简单:对象不是数据结构。至少,从用户的角度来看,它们不是数据结构。对象的用户不能看到数据,因为它是全部是私有的。这些用户只能看到该对象的公共方法。所以,从用户的角度来看,一个对象就是一组操作。
相反,数据结构是一组没有隐含行为的公共数据变量。ORM可以更好地命名为“数据映射器”,因为它们将数据从关系数据库表加载到数据结构中。
ORM系统应该放在哪里?当然在数据库层。的确,ORM构成了网关接口和数据库之间的另一种谦卑对象边界。
服务监听器
那服务呢?如果你的应用程序必须与其他服务通信,或者如果你的应用程序提供了一组服务,我们会发现谦卑对象模式创建服务边界吗?
当然!应用程序将数据加载到简单的数据结构中,然后将这些结构跨越边界传递给某些模块,再由它们适当格式化数据并将其发送到外部服务。在输入端,服务监听器将接收来自服务接口的数据并将其格式化为可供应用程序使用的简单数据结构。该数据结构然后通过服务边界传递。
小结
在每个架构边界上,我们可能会发现潜伏在附近某处的谦卑对象模式。跨越边界的交流几乎总是会涉及某种简单的数据结构,边界常常会把难以测试的东西从易于测试的东西中分离出来。在架构边界使用这种模式极大地提高了整个系统的可测试性。
1. xUnit Patterns, Meszaros, Addison-Wesley, 2007, p. 695. ↩
2. Patterns of Enterprise Application Architecture, Martin Fowler, et. al., Addison-Wesley, 2003, p. 466. ↩
3. because the gateways can be replaced with appropriate stubs and test-doubles ↩