设计模式 - 门面模式
Facade,门面设计模式,也叫外观设计模式,实现起来比较好理解,记录一下它的思想和实现
一、思想
一个系统里面总是有很多接口与服务,但有一种情况是用户希望更方便地访问一连串内部接口服务,该怎么做?
- 在这个系统里面再写一个服务,然后这个服务集成调用了系统的其他服务
- 重新写一个系统,在里面引用上边的系统,在这个新系统里面写一个服务,调用用户所需的服务
评估一下:
- 做法 1 不推荐,因为代码冗余大,服务应该是具备复用性的,这样会使得一个系统或者说是一个类变得很臃肿
- 做法 2,消除了原系统的臃肿,仅仅只是单向依赖,比较合适
上面的做法 2 ,创建的一个新系统,其实就是一个门面
那么,从中也可以大概理解门面到底是什么意思了,先看看 Wiki 中比较官方的定义
A facade is an object that serves as a front-facing interface masking more complex underlying or structural code.
👉 意思就是,一个门面就是一个服务于前端的接口,这个接口可以掩盖内部系统的复杂性代码
二、实例
那么,举个实际的例子可能更好理解
食客下馆子,馆子里有很多菜品,食客想吃红烧排骨,但红烧排骨得烹饪啊,食客得一件件告诉馆子红烧排骨怎么烹饪么?
焯水();
下糖();
生抽();
老抽();
白醋();
...
肯定不能这样,食客只是想吃而已,下个命令,“我要吃红烧排骨!”,然后你给我做出来,我吃!
食客.我要吃红烧排骨();
食客.吃红烧排骨(红烧排骨);
此时,店小二登场了!告诉店小二,我要吃红烧排骨!他就等给你端上来!你吃就完事了!
至于后台,他怎么给你的订单排序,怎么通知厨师,厨师怎么做的,这些你都不用管
所以,在这个场景中,馆子就是一个提供服务的系统,里面有很多复杂的服务,例如掌柜的结账,厨师做饭,店小二是处理订单的等等
在我们上边的例子中,店小二就是这个系统的门面,因为相对于食客,他屏蔽了内部的一系列复杂的服务,只给你一个最终的结果
三、实现
回到我们在家看电影,利用电脑,也是有一系列的操作:挑电影,下载电影,播放
那么,我们也可以建立一个门面,也就是所谓的工具人,来帮我们完成这些东西,我们直接在电脑看就好了
class Computer {
// 挑电影
public String selectMovie() {
return "最好看的电影";
}
// 下载电影
public Movie downLoadMovie(String mName) {
Movie movie = new Movie();
movie.set(mName);
return movie;
}
// 播放电影
public void playing(Movie m) {
m.playing();
}
}
class ToolMan {
private Computer computer = new computer();
// 屏蔽操作,直接获取一系列服务的结果
public Movie getMovie() {
return computer.downLoadMovie(computer.selecMovie());
}
}
class Me {
// 我利用工具人直接播放电影,不用自己操作那些其他操作
ToolMan tool = new ToolMan();
Computer.playing(tool.getMovie());
}
四、应用
4.1 SLF4J
SLF4J 不算一个新框架,它只是抽象了各种日志框架,如 Logback、Log4j、Commons-logging、JDK logging 接口,它使得用户可以在部署时使用自己想要的日志框架,所以其实它就是基于门面模式设计出来的一个整合性的框架
4.2 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;
}
// ...省略...
}
4.3 Tomcat
Request 对象中很多方法是内部组件之间相互交互使用的,比如 setComet()
、setRequestedSessionId()
等,这些不为外界调用,但必须设置为 public,因为内部还需要组件间调用,最好的方法就是通过一个 Facade 类,将内部组件之间交互使用的方法屏蔽调,只提供给外部程序感兴趣的方法,而 Tomcat 就是这么做的,这么做提高了安全性,让外部无法调用不应该调用的方法