前言
主要内容有:
- 该模式的介绍,包括:
- 引子、意图(大白话解释)
- 类图、时序图(理论规范)
- 该模式的代码示例:熟悉该模式的代码长什么样子
- 该模式的优缺点:模式不是万金油,不可以滥用模式
- 该模式的应用案例:了解它在哪些重要的源码中被使用
结构型——外观模式、门面模式 Facade Pattern
引子
门面模式便是把一些复杂的流程封装成一个接口供给外部用户更简单的使用。
比如用户是用电脑,电脑有操作:开机关机重启等。每个操作里都有复杂的逻辑,比如开始需要先启动BIOS-引导硬盘—进入系统-初始化桌面等。对于使用者来说,只需要调用开机的方法。
定义
门面模式( Facade Pattern) 也叫做外观模式, 是一种比较常用的封装模式,
Provide a unified interface to a set of interfaces in a subsystem.Facade defines a higher-level interface that makes the subsystem easier to use.( 要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。 门面模式提供一个高层次的接口, 使得子系统更易于使用。 )
类图
如果看不懂UML类图,可以先粗略浏览下该图,想深入了解的话,可以继续谷歌,深入学习:
门面模式类图:
- 客户角色(Client): 客户通过调用Facede来完成要实现的功能。
- 门面角色(Facade):它被客户角色调用,并且知道自己管理着的子系统。内部根据客户角色的需求预定了几种功能的组合。
- 子系统角色(SystemA/B/C):实现了子系统的功能。它对客户角色是完全透明的。它内部可以有系统内的相互交互,也可以由供外界调用的接口。
时序图
时序图(Sequence Diagram)是显示对象之间交互的图,这些对象是按时间顺序排列的。时序图中显示的是参与交互的对象及其对象之间消息交互的顺序。
我们可以大致浏览下时序图,如果感兴趣的小伙伴可以去深究一下:
代码实现
门面Facade:
public class Computer { public static final Logger LOGGER = Logger.getLogger(Computer.class); private CPU cpu; private Memory memory; private Disk disk; public Computer() { cpu = new CPU(); memory = new Memory(); disk = new Disk(); } public void start() { LOGGER.info("Computer start begin"); cpu.start(); disk.start(); memory.start(); LOGGER.info("Computer start end"); } public void shutDown() { LOGGER.info("Computer shutDown begin"); cpu.shutDown(); disk.shutDown(); memory.shutDown(); LOGGER.info("Computer shutDown end..."); } } 复制代码
客户端调用Client:
public class Client { public static final Logger LOGGER = Logger.getLogger(Client.class); public static void main(String[] args) { Computer computer = new Computer(); computer.start(); computer.shutdown(); } 复制代码
对于用户来说,使用Client完全不需要关心底层细节。
使用场景举例
典型例子:SLF4J 中的外观模式
SLF4J 是简单的日志外观模式框架,抽象了各种日志框架例如 Logback、Log4j、Commons-logging 和 JDK 自带的 logging 实现接口。它使得用户可以在部署时使用自己想要的日志框架。
SLF4J 没有替代任何日志框架,它仅仅是标准日志框架的外观模式。如果在类路径下除了 SLF4J 再没有任何日志框架,那么默认状态是在控制台输出日志。
spring jdbc中的外观模式
查看 org.springframework.jdbc.support.JdbcUtils
Mybatis中的外观模式
查看 org.apache.ibatis.session.Configuration 类中以 new 开头的方法
public class Configuration { public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new CachingExecutor(executor); } executor = (Executor) interceptorChain.pluginAll(executor); return executor; } public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) { ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds); resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler); return resultSetHandler; } // ...省略... } 复制代码
Tomcat 中的外观模式
org.apache.catalina.connector.Request 和 org.apache.catalina.connector.RequestFacade 这两个类都实现了 HttpServletRequest 接口
优缺点
优点
- 松散耦合:使得客户端和子系统之间解耦,让子系统内部的模块功能更容易扩展和维护;
- 简单易用:客户端根本不需要知道子系统内部的实现,或者根本不需要知道子系统内部的构成,它只需要跟Facade类交互即可。
- 更好的划分访问层次:有些方法是对系统外的,有些方法是系统内部相互交互的使用的。子系统把那些暴露给外部的功能集中到门面中,这样就可以实现客户端的使用,很好的隐藏了子系统内部的细节。
缺点
- 在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。