书接上回,本篇讲一下结构型模式-外观设计模式
外观设计模式
外观模式又叫门面模式
定义:为子系统中的一组接口提供一个一致的界面,它定义了一个高层接口,这个接口使得这一子系统更加容易使用。(乍一看,还真不知道扯的是啥,文绉绉的。)
要解释一下名词:
子系统:可以理解为实现功能的各种组件(类、接口等)
界面:facade翻译过来就是界面的意思,有些人也翻译成门面。从代码上讲,可以理解为一个统一的api(可以是类可以是接口)
大白话:facade外观模式就是一种能遮蔽子系统(api操作/实现)复杂性,使用一套更简便api统一操作模式。
看下图:
其中的Facade就是界面/统一的API。
案例分析
需求:3个子系统,要求执行顺序A--->B--->C
普通版
子系统
public class SubSystemA {
void doA(){
System.out.println("执行了A操作....");
}
}
public class SubSystemB {
void doB(){
System.out.println("执行了B操作....");
}
}
public class SubSystemC {
void doC(){
System.out.println("执行了c操作....");
}
}
测试
public class App {
public static void main(String[] args) {
SubSystemA a = new SubSystemA();
SubSystemB b = new SubSystemB();
SubSystemC c = new SubSystemC();
//A-->B-->
a.doA();
b.doB();
c.doC();
}
}
执行了A操作....
执行了B操作....
执行了c操作....
解析
上面代码实现需求约定:A-->B--->C, 这种写法从设计层面上分析是否存在问题呢?
答案是有,
1>客户端App执行A-->B--->C前提必须完全了解SubSystemA/B/C 3个子系统,同时知道怎么调用对应的方法,最终才能实现需求。
2>SystemA/B/C 3个子系统其实已经对App暴露所有代码细节啦,从架构设计角度看,应用层不应该知道低层实现细节。因为:迪米特原则,最少知道原因
怎么改进?外观模式
简单外观
需求:3个子系统,要求执行顺序A--->B--->C
SubSystemA/B/C 个类不变, 添加一个Facade类。
Facade类
public class Facade {
public void doWork(){
SubSystemA a = new SubSystemA();
SubSystemB b = new SubSystemB();
SubSystemC c = new SubSystemC();
//A-->B-->
a.doA();
b.doB();
c.doC();
}
}
测试
public class App {
public static void main(String[] args) {
Facade facade = new Facade();
facade.doWork();
}
}
解析
从代码上仅仅是将执行逻辑拷贝到到Facade类执行而已,这个跟之前直接在App上操作好像没有啥区别?还是有区别,这种做法带来个好处
1>Facade类遮蔽客户端与系统内部模块交互(SubSystemA/B/C),把SubSystemA/B/C 模块组成一个整体,开放Facade类统一对外。这操作不但方便客户端调用,也封装了系统内部细节。也就是说,Facade类与各个模块的交互已经是内部行为啦,即使后续ABC调用顺序发生该变,只需要内部调整即可,客户端完全无须变动。
2>提高代码复用性,简化开发。当多个客户端需要使用SubSystemA/B/C功能时,只需要调用Facade类doWork方法即可,无须各种new, 各种子系统调用。
3>减少学习成,从学习3个子系统操作,变为学习一个Facade类。
抽象版
普通版的外观模式还是一定问题,比如:
子系统执行顺序:A-->B--->C 要调整为 C-->B-->A, 怎么办?
此时需要直接修改Facade类doWork代码,一旦这么操作,就违背了开闭原则(扩展开发,修改关闭) 怎办?--->抽象外观模式
SubSystemA/B/C 类不变
IFacade接口
public interface IFacade {
void doWork();
}
FacadeImplABC
public class FacadeImplABC implements IFacade {
public void doWork(){
SubSystemA a = new SubSystemA();
SubSystemB b = new SubSystemB();
SubSystemC c = new SubSystemC();
//A-->B-->C
a.doA();
b.doB();
c.doC();
}
}
FacadeImplCBA
public class FacadeImplCBA implements IFacade {
public void doWork(){
SubSystemA a = new SubSystemA();
SubSystemB b = new SubSystemB();
SubSystemC c = new SubSystemC();
//C-->B-->A
c.doC();
b.doB();
a.doA();
}
}
测试
public class App {
public static void main(String[] args) {
FacadeImplABC abc = new FacadeImplABC();
FacadeImplCBA cba = new FacadeImplCBA();
abc.doWork(); //ABC
cba.doWork(); //CBA
}
}
解析
后续需要调整ABC执行顺序, 只需要new 出新的Facade即可。UML图中画了FacadeFactory类,起来将FacadeImpl类跟工厂方法设计模式混合使用。
适用场景
1>给复杂子系统提供统一的简单接口时,可以使用
2>想让客户端与实现类存在松散耦合时,可以使用。
3>想构建多层结构系统时,可以使用。
优缺点
优点
简化调用过程,无需深入了解子系统
减少系统依赖,设计出松散耦合
更好的划分访问层次,隐藏内部实现细节
符合迪米特法则,即最少知道原则
缺点
增加子系统,拓展子系统行为容易引入风险
不符合开闭原则
开发案例
外观模式运用非常广泛,从JDK里面找找-HttpClientFacade
jdk.internal.net.http 包下一个类进行http访问的
final class HttpClientFacade extends HttpClient implements Trackable {
final HttpClientImpl impl;
/**
* Creates an HttpClientFacade.
*/
HttpClientFacade(HttpClientImpl impl) {
this.impl = impl;
}
@Override // for tests
public Tracker getOperationsTracker() {
return impl.getOperationsTracker();
}
@Override
public Optional<CookieHandler> cookieHandler() {
return impl.cookieHandler();
}
@Override
public SSLContext sslContext() {
return impl.sslContext();
}
.....
}
其中的子系统对象是:HttpClientImpl
除了上面之外,在Tomcat中也能找到非常经典使用-RequestFacade
Tomcat传给Servlet 或者springmvc的controller中request并不是原生的request对象而是包装之后的,也就是RequestFacade
public class RequestFacade implements HttpServletRequest {
protected Request request = null;
protected static final StringManager sm = StringManager.getManager(RequestFacade.class);
public RequestFacade(Request request) {
this.request = request;
}
public Object getAttribute(String name) {
if (this.request == null) {
throw new IllegalStateException(sm.getString("requestFacade.nullRequest"));
} else {
return this.request.getAttribute(name);
}
}
public String getContentType() {
if (this.request == null) {
throw new IllegalStateException(sm.getString("requestFacade.nullRequest"));
} else {
return this.request.getContentType();
}
}
public ServletInputStream getInputStream() throws IOException {
if (this.request == null) {
throw new IllegalStateException(sm.getString("requestFacade.nullRequest"));
} else {
return this.request.getInputStream();
}
}
.....
}
其中的子系统对象是:Request
总结
外观模式本质:封装交互,简化调用。