> 文档中心 > 阿里资深工程师教你门面模式

阿里资深工程师教你门面模式

结构型。

应用场景明确,主要在接口设计方面,以兼顾接口的易用性、通用性。

1 接口粒度

为保证接口可复用性(或通用性),需将接口设计尽量地细粒度&&职责单一:

  • 接口粒度过小,接口使用者就需调用n多个细粒度接口才能完成。抱怨接口繁琐难用
  • 接口粒度太大,一个接口返回n多数据,要做n多事情,导致接口不够通用、可复用性不好。接口不可复用,那针对不同的调用者的业务需求,就需开发不同接口来满足,这就会导致系统的接口无限膨胀

本文就教你如何解决接口可复用性和易用性矛盾。

2 原理

Facade Design Pattern,也叫外观模式,在GoF的《设计模式》中定义:

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.

门面模式为子系统提供一组统一接口,定义一组高层接口让子系统更易用。

不同应用场景下,使用门面模式的意图也不同。

3 应用场景

“门面模式让子系统更加易用”,门面模式定义中的“子系统(subsystem)”也有多种理解方式。既能是个完整系统,也能是更细粒度的类或模块。

3.1 解决易用性问题

封装系统的底层实现,隐藏系统复杂性,提供一组更简单易用、更高层接口。如:

  • Linux系统调用函数看作“门面”。它是Linux os暴露给开发者的一组“特殊”编程接口,封装底层更基础的Linux内核调用
  • Linux的Shell命令,继续封装系统调用,提供更友好、简单命令,让我们直接通过执行命令和os交互

从隐藏实现复杂性,提供更易用接口的意图,门面模式类似迪米特法则(最少知识原则)和接口隔离原则:两个有交互的系统,只暴露有限的必要接口。

门面模式还类似封装、抽象设计思想,提供更抽象的接口,封装底层实现细节。

3.2 解决性能问题

如系统A,提供a、b、c、d接口。系统B完成某功能,需调用A系统a、b、d接口。利用门面模式,提供一个包裹a、b、d接口调用的门面接口x,给系统B直接使用。

让B直接调用a、b、d也没啥啊,为啥还提供个包裹a、b、d的接口x?

假设A是服务器,B是客户端。客户端通过服务器提供的接口获取数据。为提高客户端响应速度,要尽量减少客户端与服务器之间的网络通信次数。

若某业务功能(如显示某页面信息)需“依次”调用a、b、d三接口,因业务特点,不支持并发调用这三接口。现在发现客户端响应较慢,排查发现,因过多接口调用导致过多的网络通信。利用门面模式,让服务器提供一个包裹a、b、d三个接口调用的接口x。客户端调用一次接口x,就获取到所有想要数据,将网络通信次数从3次减少到1次,提高客户端响应速度。

这就是门面模式的一个用途,即解决性能问题。

将多个接口调用替换为一个门面接口调用,减少网络通信成本,提高App客户端的响应速度。

从代码实现角度

如何组织门面接口、非门面接口?

若门面接口不多,完全可将其和非门面接口放一块,也无需特殊标记,当作普通接口用即可。

若门面接口很多,可基于已有接口,再抽象一层,专门放置门面接口,从类、包的命名上跟原来的接口层做区分。

若门面接口特多,且很多都是跨多个子系统,可将门面接口放到一个新子系统。

3.3 分布式事务

有用户、钱包两个业务域,都对外暴露一系列接口,如用户的crud、钱包的crud。用户注册时,不仅创建用户(User表),还给用户创建一个钱包(Wallet表)。

可依次调用用户的创建接口、钱包的创建接口。但用户注册需支持事务,即创建用户和钱包的两个操作,都成功/失败。

要支持两个接口调用在一个事务执行,很难实现,涉及分布式事务。分布式事务框架或补偿机制复杂度都较高。

最简单的,利用DB事务或Spring事务,在一个事务中,执行创建用户、创建钱包两个SQL操作。就要求两个SQL操作要在一个接口完成,使用门面模式,即再设计一个包裹这两个操作的新接口,让新接口在一个事务中执行两个SQL操作。

可若再抽离一个门面模式的子模块,里面肯定要包含用户/钱包数据源。然后用户、钱包系统又是独立子模块,这样一个数据源散到很多地方? 若用户表、钱包表要拆离成两套库就GG。没错,但若用门面包装一个接口,使用分布式事务框架解决事务问题,其他多个业务就只用调用这个接口,无需自己实现分布式事务问题。相当于复用解决事务问题的代码。

这种门面接口在多租户系统常见,如初始化一个租户分很多步:

  • 创建租户
  • 创建租户的各种配置
  • 创建租户下的用户
  • 完毕后给租户管理员推送已完毕的信息(邮件,短信等)

有时,为易用性和交易一致性,就是要把这些步骤封装在一个接口里(其实每一步都有一个接口)。

4 V.S 适配器模式

  • 适配器,接口转换,解决原接口和目标接口不匹配问题
  • 门面模式,接口整合,解决多接口调用带来的问题

5 总结

类、模块、系统之间的“通信”,一般都是通过接口调用。接口设计的好坏,直接影响到类、模块、系统是否好用。完成接口设计,就相当于完成了一半的开发任务。只要接口设计得好,那代码就不会太差。

接口粒度设计得太大,太小都不好:

  • 太大会导致接口不可复用
  • 太小会导致接口不易用

接口可复用性和易用性需要权衡,基本处理原则:尽量保持接口的可复用性,但针对特殊情况,允许提供冗余的门面接口,来提供更易用的接口。