门面模式:如何设计合理的接口粒度以兼顾接口的易用性和通用性
接口职责单一可能会被拆的粒度过于细,造成接口数量比较多,调用者使用起来较为繁琐,易用性降低。
太宽泛可能职责糅合,复用性很差。
原理和实现
-
门面模式为子系统提供一组统一的接口,定义一组高层接口让子系统更易用。
Provide a unified interface to a set of interfaces in a subsystem. Facade Pattern defines a higher-level interface that makes the subsystem easier to use.
-
假设我们有A系统,abcd四个接口,系统B完成某个业务需要A系统的abd接口,我们可以利用门面模式将abd封装为一个接口x给B使用。
-
为什么不直接调用呢? abd三个小接口单独调用看起来还好。
-
实际情况是A是一个后端服务器,B是App客户端,B对于abd 的调用都需要通过网络通信,而网络通信在整个流程中可能会占据较大的时间比例。为了减少网络通信次数我们才把他们封装成一个接口。(解决性能问题只是门面模式的一个应用场景)。
应用场景举例
1. 解决易用性问题
- Facade pattern可以用来封装系统的底层实现,隐藏系统的复杂性,通过一组更加简单易用的、更高层的接口。例如Linux的shell命令可以看做是一种门面模式的应用。它封装系统调用,提供更加友好简单的命令让我们可以跟操作系统直接交互。
- 门面模式背后的逻辑和迪米特法则(最少知识原则)和接口隔离原则很契合: 两个有交互的吸引,只暴露有限、必要的接口。
- 除此之外,门面模式也与封装、抽象的设计思想契合。
2. 解决性能问题
- 如上一节 的例子一样。
- 从代码实现的角度:如果门面接口不多,可以将其与非门面接口放在一起,当做普通接口来用。如果门面接口很多,可以在已有接口智商再重新抽象出一层,专门放置门面接口,从类、包的命名上跟原来的接口层作区分。
3. 解决分布式事务问题
-
看个例子:
在一个金融系统中,有两个业务领域模型,用户和钱包。这两个业务领域模型都对外暴露了一系列接口,比如用户的增删改查接口、钱包的增删改查接口。假设有这样一个业务场景:在用户注册的时候,我们不仅会创建用户(在数据库 User 表中),还会给用户创建一个钱包(在数据库的 Wallet 表中)。对于这样一个简单的业务需求,我们可以通过依次调用用户的创建接口和钱包的创建接口来完成。但是,用户注册需要支持事务,也就是说,创建用户和钱包的两个操作,要么都成功,要么都失败,不能一个成功、一个失败。
-
要支持这种事务,是比较难实现的。可以引入分布式事务框架、或者事后补偿机制来解决,但是这些代码实现有点复杂。最简单的就是利用数据库事务或者Spring中的事务,在一个事务中执行创建用户和创建钱包的这两个SQL操作。所以这个地方可以借助门面模式设计一个接口包含这两个操作。