6 软件架构
6.9 展示器和谦卑对象
在《架构整洁之道-软件架构-策略与层次、业务逻辑、尖叫的软件架构、整洁架构》有我们提到了展示器(presenter),展示器实际上是采用谦卑对象(humble object)模式的一种形式,这种设计模式可以很好的帮助识别和保护系统架构的边界。
谦卑对象模式最初的设计目的是帮助单元测试的编写者区分容易测试的行为与难以测试的行为,并将它们隔离。其设计思路非常简单,就是将这两类行为拆分成两组模块或类。其中一组模块被称为谦卑(Humble)组,包含了系统中所有难以测试的行为,而这些行为已经被简化到不能再简化了。另一组模块则包含了所有不属于谦卑对象的行为。
例如,GUI通常是很难进行测试的,因为让计算机自行检视屏幕内容,并检查指定元素是否出现是非常困难的,然后,GUI中的大部分行为实际上是很容易被测试的。这时候,我们可以利用谦卑对象模式将GUI的这两种行为拆分成展示器与视图两部分。
视图部分属于难以测试的谦卑对象,这种对象的代码通常应该越简单越好,它只应负责将数据填充到GUI上,而不应该对数据进行任何变更。应用程序所能控制的、要在屏幕上显示的一切东西,都应该在视图模型中以字符串、布尔值或枚举值的形式存在,视图部分除了加载视图模型所需要的值,不应该再做任何其他的事情。因此,视图是谦卑对象。
展示器则是可测试的对象,展示器的工作是负责从应用程序中接收数据,然后按视图的需要将这些数据格式化,以便视图将其呈现在屏幕上。
众所周知,强大的可测试性是一个架构的设计是否优秀的显著衡量标准之一。 谦卑对象模式就是这方面的一个非常好的例子。我们将系统分割成可测试和不可测试两部分的过程也就定义了系统的架构边界。展示器与视图之间的边界只是多种架构边界的一种,另外还有许多其他边界:
(1) 数据库网关:对于用例交互器(interactor)与数据库中间的组件,我们通常称之为数据库网关。这些数据库网关本身是一个多态接口,包含了应用程序在数据库上所要执行的创建、读取、更新、删除等所有操作。SQL不应该出现在用例层中,所以这部分的功能就需要通过网关接口来提供,而这些接口的实现在数据库层中,这些实现属于谦卑对象,它们应该只利用SQL或其他数据库提供的接口来访问所需要的数据。与之相反,交互器则不属于谦卑对象,因为它们封装的是特定应用场景下的业务逻辑,它是可测试的;
(2) 数据映射器:实际上就是Hibernate这类的ORM框架,ORM框架将数据从关系型数据库加载到了对应的数据结构中,在数据库和数据库网关接口之间构建了另一种谦卑对象的边界,它属于数据库层的模块;
(3) 服务监听器:当我们的应用程序需要与其他服务进行某种交互,或者该应用本身要提供某一套服务时,我们的应用程序会将数据加载到简单的数据结构中,并将这些数据结构跨边界传输给那些能够将其格式化并传递到其他外部服务的模块,而在输入端,服务监听器会负责从服务接口中接收数据,并将其格式化成该应用程序易用的格式,以实现跨服务边界的传输;
在每个系统架构的边界处,都有可能发现谦卑对象模式的存在,因为跨边界的通信肯定要用到某种简单的数据结构,而边界会自然而然地将系统分割成难以测试的部分与容易测试的部分,所以通过在系统的边界处运用谦卑对象模式,可以大幅地提高整体系统的可测试性。
6.10 不完全边界
构建完整的架构边界是一件很耗费成本的事,在这个过程中,需要为系统设计双向的多态边界接口,用于输入和输出的数据结构,以及所有相关的依赖关系管理,以便将系统分割成可独立编译与部署的组件。
在很多情况下,非常优秀的架构师也会认为设计架构边界的成本太高了,但为了应对将来可能的需要,通常还是希望预留一个边界。但这种预防性设计在敏捷社区里是饱受诟病的,因为它显然违背了YAGNI原则(You Aren’t Going to Need It),然而,架构师的工作本身就是要做这样的预见性设计,因此,我们引入了不完全边界(partial boundary)的概念。
构建不完全边界的一种方式就是在将系统分割成一系列可独立编译、独立部署的组件之后,再把它们构建成一个组件。换句话说,在将系统中所有的接口、用于输入/输出的数据格式等每一件事都设置好之后,仍选择将它们统一编译和部署为一个组件。
显然,这种不完全边界所需要的代码量以及设计的工作量,和设计完整边界时是完全一样的。但它省去了多组件管理这部分