外观模式是什么?
外观模式(Facade Pattern)是一种结构型的设计模式,它为子系统的一组接口提供一个统一的高层次接口,让子系统更容易使用。外观模式的本质就是封装,它屏蔽了一组复杂接口的调用,而只暴露一个简单的接口,简化了使用者的操作。生活中,运用外观模式的例子比比皆是,比如使用手机拍照,我们只要轻轻地点下按键,就自动地生成一张照片。而手机的感光器件是如何运作的、光线是怎么转换成数字信号的、图像数据是如何保存的等等这些细节,我们通通都不需要了解。
UML 类图
用 UML 类图来描述外观模式的结构,在模式中各个角色之间的关系:
根据上图,总结了模式中各个角色的职责以及它们之间的关系:
- 外观提供了一个便捷的方式完成一组复杂的子系统操作,它知道如何串联子系统的各种部件来完成外部的请求。
- 附加的外观可以避免不相关的功能污染单一的外观,使其变成又一个复杂的结构。
- 复杂的子系统由多个不同的对象构成,要运用这些对象完成一件有意义的工作,你需要深入了解它们的细节,并使用正确的方式来调度它们。
- 客户端通过访问外观代替对子系统的直接调用。
案例
让我们通过一个案例来帮助我们进一步理解外观模式。一般的软件或网站都有登录的功能,用户在登录的时候,通常只需要输入用户名和密码即可。但是软件系统要做的事可就多了,比如:校验用户名密码是否正确、校验用户状态是否正常、设置会话、记录日志等。这些操作一般是分散在软件系统的各个模块之中,登录界面在接收到用户的请求时(用户输入用户名、密码后点击了登录按钮),它需要一一调用相关的模块,才能完成登录操作,例如:
userModule.checkUserIsExistent(username);
userModule.checkPasswordIsMatched(username, password);
userModule.checkUserIsAvailable(user);
sessionModule.generateSessionId();
sessionModule.saveSession(sessionId, user);
loggerModule.log(loginEvent);
但是,这样登录界面就要与系统的多个模块紧密耦合在一起,而它其实并不关心这些操作具体是如何实现的,它其实就只关注一件事:登录的结果。我们可以为登录操作创建一个外观,仅暴露出一个简单的接口,由它来完成这个复杂的登录流程,并返回登录界面想要的结果。
public class LoginFacade {
private UserModule userModule = new UserModuleImpl();
private LoggerModule loggerModule = new LoggerModuleImpl();
private SessionModule sessionModule = new SessionModuleImpl();
public LoginResult login(String username, String password) {
if (!userModule.isUserExistent(username)) {
return LoginResult.fail("Counld not find the user[" + username + "].");
}
Optional<User> userOpt = userModule.isPasswordMatched(username, password);
if (!userOpt.isPresent()) {
return LoginResult.fail("The password is not matched.");
}
User user = userOpt.get();
if (!user.isNormal()) {
return LoginResult.fail("The user is unavailable.");
}
String sessionId = sessionModule.generateSessionId();
sessionModule.saveSession(sessionId, user);
loggerModule.log(new LoginEvent(username));
return LoginResult.success("Login successfully.");
}
}
现在,登录界面在接收到用户输入的用户名、密码之后,只需要调用登录外观的接口,就可以完成登录的一系列操作。
LoginFacade loginFacade = new LoginFacade();
LoginResult loginResult = loginFacade.login("Huey", "123456");;
案例源码
可在 GitHub 上查看上述案例的完整代码。
参考资料
本文参考的资料如下:
- Head First Design Patterns - Elisabeth Freeman, Chapter 7.
- https://www.baeldung.com/java-facade-pattern
- https://refactoring.guru/design-patterns/facade
- https://refactoringguru.cn/design-patterns/facade