本文最后更新于:2020年12月27日 中午
类型:结构型模式
意图:定义一个统一的系统接口,隐藏多个子系统的复杂性。
主要解决:降低访问系统客户端内部子系统的复杂度。
使用场景:解耦合客户端与具体子系统,子系统与客户端无需感知对方,客户端不需要亲自调用子系统的接口,把原本需要与多个子系统的交互全部封装到门面类中,客户端只需要和 Facade 类交互即可。
设计模式系列文章目录
角色
- Facade: 门面角色,委托客户端的请求到具体的客户端
- SubSystem:子系统角色
UML
使用门面模式封装之前 UML:
使用门面模式封装之后 UML:
可以看到客户端由依赖三个对象变成依赖一个对象,通过门面对象封装子系统,对于客户端,屏蔽子系统内部的复杂性。
需要注意的是,门面类不要参与任何具体的业务逻辑。
实战
在大量使用 Dubbo 进行接口调用的项目系统中,有很多 Dubbo 方法是属于公用的方法,虽然客户端类可以注入不同的 Dubbo 服务类进行接口调用,但是明显客户端与其他子系统的依赖过于紧密。
当一个子系统需要进行修改时,需要修改所有调用此系统的接口,此时最适合使用门面模式来封装这些子系统的接口,然后统一对客户端提供方法进行调用,大大降低客户端与子系统间的耦合性。
本文为了简化示例,就不未引入 Dubbo,而是直接 Mock 了两个远程服务类。示例提供一个针对获取用户相关信息的门面类。
示例代码
定义两个 Mock 的远程服务类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @Service public class MockUserRemoteService {
public boolean isNewUser(Long userId) { return true; }
public UserInfo getUserInfo(Long userId) { return new UserInfo(); } }
@Service public class MockDeptRemoteService { public DeptInfo getDeptInfo(Long userId) { return new DeptInfo(); } }
|
定义用户门面类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| @Component public class UserFacade { @Resource private MockUserRemoteService userRemoteService; @Resource private MockDeptRemoteService deptRemoteService;
public boolean isNewUser(Long userId) { return userRemoteService.isNewUser(userId); }
public UserInfo getUserInfo(Long userId) { return userRemoteService.getUserInfo(userId); }
public DeptInfo getDeptInfo(Long userId) { return deptRemoteService.getDeptInfo(userId); }
}
|
测试客户端调用
| @RunWith(SpringRunner.class) @SpringBootTest(classes = App.class) public class FacadeTest { @Resource protected UserFacade userFacade;
@Test public void test1() { long userId = 11L; UserInfo userInfo = userFacade.getUserInfo(userId); DeptInfo deptInfo = userFacade.getDeptInfo(userId); } }
|
总结
门面模式还是很好理解的,封装了子系统的复杂性,通过门面对象只暴露客户端需要的接口,一些内部的接口可以做到对客户端隐藏。
门面模式的优点
门面模式的优点:
● 松散耦合
门面模式松散了客户端与子系统的耦合关系,让子系统内部的模块能更容易扩展和维护。
● 简单易用
门面模式让子系统更加易用,客户端不再需要了解子系统内部的实现,也不需要跟众多子系统内部的模块进行交互,只需要跟门面类交互就可以了。
● 更好的划分访问层次
通过合理使用 Facade,可以帮助我们更好地划分访问的层次。有些方法是对系统外的,有些方法是系统内部使用的。把需要暴露给外部的功能集中到门面中,这样既方便客户端使用,也很好地隐藏了内部的细节。
源码下载
参考