类型:结构型模式

意图:定义一个统一的系统接口,隐藏多个子系统的复杂性。

主要解决:降低访问系统客户端内部子系统的复杂度。

使用场景:解耦合客户端与具体子系统,子系统与客户端无需感知对方,客户端不需要亲自调用子系统的接口,把原本需要与多个子系统的交互全部封装到门面类中,客户端只需要和 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) {
// TODO 调用远程接口
return true;
}

public UserInfo getUserInfo(Long userId) {
// TODO 调用远程接口
return new UserInfo();
}
}

@Service
public class MockDeptRemoteService {
public DeptInfo getDeptInfo(Long userId) {
// TODO 调用远程接口
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);
}

}

测试客户端调用

1
2
3
4
5
6
7
8
9
10
11
12
13
@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,可以帮助我们更好地划分访问的层次。有些方法是对系统外的,有些方法是系统内部使用的。把需要暴露给外部的功能集中到门面中,这样既方便客户端使用,也很好地隐藏了内部的细节。

源码下载

参考