简介
外观模式 (Facade, 门面模式), 为子系统中额外一组接口提供一个一致的界面, 此模式定义了一个高层接口, 这个接口使得这一子系统更加容易使用.
在项目开发和实际运用中十分频繁, 但是其极易理解
图例:
外观不只是简化了接口, 也将客户从组件的子系统中解耦. 外观和适配器可以包装许多类, 但是外观的意图是简化接口, 而适配器的意图是将接口转换成不同接口.
场景设置
似曾相识, 有个超级复杂的论坛系统 (客户端调用的相当复杂).
下面我们将直接看看旧代码怎么写的吧
UserController 主流程类
- package facade;
- public class UserController {
- public static void main(String[] args) {
- // 获取昵称
- UserInfo userInfo = new UserInfo();
- String userName = userInfo.getNickName();
- // 获取最近登录时间
- UserSystem userSystem = new UserSystem();
- String lastLoginTime = userSystem.lastLoginTime();
- // 获取用户点赞数
- UserBehavior userBehavior= new UserBehavior();
- int likeNum = userBehavior.getLikeNum();
- //todo
- // 其它很多小类组成了供前端渲染的数据
- System.out.println("用户名" + userName + ", 最近登录时间" + lastLoginTime+", 喜欢了"+likeNum+"篇帖子");
- }
- }
相关小类
用户信息相关
- package facade;
- public class UserInfo {
- public String getNickName(){
- return "许仙啊";
- }
- }
用户系统相关:
- package facade;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- public class UserSystem {
- public String lastLoginTime() {
- SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- return df.format(new Date());
- }
- }
用户行为相关:
- package facade;
- public class UserBehavior {
- public int getLikeNum() {
- return 5;
- }
- }
output:
用户名许仙啊, 最近登录时间 2019-01-15 23:08:45, 喜欢了 5 篇帖子
由此结构我们可以看出, 对于我们的控制器来说是比较复杂的, 对象涉及的比较多, 当业务比较越来越大的时候, 很容易出现耦合, 无法拆分, 只能在控制器中另创方法.
外观模式改造
核心 - 外观对象
- package facade;
- public class Facade {
- // 用户信息对象
- private UserInfo userInfo;
- // 用户系统信息对象
- private UserSystem userSystem;
- // 用户行为对象
- private UserBehavior userBehavior;
- public Facade() {
- userInfo = new UserInfo();
- userSystem = new UserSystem();
- userBehavior = new UserBehavior();
- }
- public String getUserInfo() {
- String userName = userInfo.getNickName();
- String lastLoginTime = userSystem.lastLoginTime();
- int likeNum = userBehavior.getLikeNum();
- //todo
- // 其它很多小类组成了供前端渲染的数据
- // 可以按类别拆分到不同方法, 也可以按功能需求一起, 需要个人思考
- return "用户名" + userName + ", 最近登录时间" + lastLoginTime+", 喜欢了"+likeNum+"篇帖子";
- }
- }
调用:
- package facade;
- public class UserController {
- public static void main(String[] args) {
- // 外观对象
- Facade facade = new Facade();
- System.out.println(facade.getUserInfo());
- }
- }
output:
用户名许仙啊, 最近登录时间 2019-01-15 23:19:16, 喜欢了 5 篇帖子
UML 图 (图片来自网络)
我们由前置控制器移动到了 getUserInfo 方法中集体封装了. 可能感觉并没有什么好处, 但是它完美的体现了依赖倒转原则和迪米特法则的思想.
归纳总结
看到这里相信你敢相信这也算设计模式. 其实外观模式有很多种灵活的拆分方式, 对于封装系统的复杂度, 提供更好的小类小功能的管理还是有非常大的用处的.
上文提到了迪米特法则, 指的是如果两个类不必直接通信, 那么这两个类就不应当发生直接的相互作用. 如果其中一个类需要调用另一个类的某一个方法的话, 可以通过第三者转发这个调用 (此文的第三者指的就是我们的外观类) -- 摘自《大话设计模式》
什么时候应该使用呢
其实这是个层与层关系的问题, 我们的控制器层和我们的系统实现层, 随着业务的复杂度上升, 他们之间的关系也会越来越复杂.
增加外观 Facade 层, 对上层提供一个简单的接口, 可以减少他们的依赖.
当维护一个遗留大型系统的时候, 很难再此基础上拓展它难以维护下去, 可能使用外观模式是非常好的一个选择.
最后谈一下和适配器模式的区别吧
区别是适配器只是适配一个类, 外观模式作用多个类?
答案不对, 适配器可以适配多个类, 外观模式也可以只服务一个复杂的类. 主要他们的意图是不一样的,
适配器模式的意图是改变接口符合客户的预期
外观模式的意图是提供子系统的一个简化接口.
参考《大话设计模式》 《Head First 设计模式》
来源: https://juejin.im/post/5c3deee4e51d4552276d8285