外观模式 Facade
外观模式 Facade
外观模式(Facade):也称门面模式,为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统在客户端使用的时候更加容易使用。
The Facade Design Patternis a structural design pattern that provides a unified interface to a set of interfaces in a subsystem.
The facade defines a higher-level interface that makes the subsystem easier to use.
外观模式角色很简单:
⨳ Subsystem(子系统):如SubsystemA
, SubsystemB
, 和 SubsystemC
是子系统的各个组件,它们包含了具体的功能实现,但是对客户端是不可见的。
⨳ Facade (外观对象),它提供了一个简单的接口给客户端,隐藏了子系统的复杂性。它包含了对子系统中各个接口的引用,并且提供了简化的方法来实现客户端需求。
上面 UML 类图拿适配器模式来类比的话,Facade
既是是想要转换成为的接口,也是转换 Subsystem
的适配器,确实相差不大。
确定相差不大吗?下面看一下具体实现。
基本实现
子系统 Subsystem
每个子系统都可以被客户端直接调用,或者被门面角色调用。
▪ 子系统A SubsystemA
public class SubsystemA {
public void methodA(){
System.out.println("调用子系统A的方法A");
}
}
▪ 子系统B SubsystemB
public class SubsystemB {
public void methodB(){
System.out.println("调用子系统B的方法B");
}
}
▪ 子系统C SubsystemC
public class SubsystemC {
public void methodC(){
System.out.println("调用子系统C的方法C");
}
}
这里需要注意的是,虽然子系统类都叫 Subsystem
什么什么的,但它们相互之间并不要求有任何关联,它们也都不是同一个接口的实现,它们各自的方法也不要求相互关联。
外观 Facade
外观类,它需要了解所有子系统的方法或属性,进行组合,以被客户端调用。
public class Facade {
private SubsystemA subsystemA;
private SubsystemB subsystemB;
private SubsystemC subsystemC;
public Facade(){
this.subsystemA = new SubsystemA();
this.subsystemB = new SubsystemB();
this.subsystemC = new SubsystemC();
}
public void facadeMethod(){
subsystemA.methodA();
subsystemB.methodB();
subsystemC.methodC();
}
}
客户端 CLient
Facade
类中的 facadeMethod()
方法是一个统一的接口,它可以调用子系统中的多个方法来完成一个任务。
Facade facade = new Facade();
facade.facadeMethod();
输出结果如下:
调用子系统A的方法A
调用子系统B的方法B
调用子系统C的方法C
上面的基本代码实现是不是很眼熟,很像 Controller - Service - Dao
三层模型,系统的代码按照功能划分为三个层次,每个层次承担不同的责任和功能,请求只和 Controller
层打交道,Controller
是 Service
的门面,Service
是 Dao
的门面。
其实设计模式是为了解决某一特定问题而出现的,不能为了设计而设计,下面看一下门面模式在源码中的运用,也许会对设计模式有更深的体会。
源码鉴赏
Tomcat 之 RequestFacade
Tomcat 作为 WEB 容器,可以将客户端发送过来的请求封装成 ServletRequest,对于 HTTP 请求则是封装成 HttpServletRequest 的子类 Request。
这个 Request 作为请求的完整封装,是给 Tomcat 内部使用的,如果将其交给用户自定义的 HttpServlet 会很危险,所以就搞了一个门面将其包装了起来,只暴露有限的方法给客户使用。
用户可以通过继承 HttpServlet 自定义 Servlet 处理请求:
public class ServletDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {}
}
方法入参 HttpServletRequest 其实是 RequestFacade。同理,给客户使用的 HttpServletResponse 是 ResponseFacade。
这么说的话,门面、门面 不就是个包装嘛,代理也是包装、装饰者也是包装、适配器也是包装....
把思想放开之后,会发现到处都是都是门面模式,准确的说是包装模式,只要能解决问题,方便维护,即使用不规范的设计模式,甚至不用设计模式,也没什么大不了的。
总结
一句话总结,门面模式的目的是让子系统更加易用!
其实门面模式在维护老系统时很好用,老系统不想改但还想用它原本的方法,那就是开发一个门面类,将老系统复杂的逻辑封装起来,让新系统只与门面交互。
门面模式优点如下:
⨳ 简化接口: 门面模式提供了一个统一的接口,隐藏了系统的复杂性,使客户端能够更容易地与系统交互。
⨳ 降低耦合度: 客户端不需要了解系统的内部实现细节,只需要与门面对象进行交互,从而降低了客户端和系统之间的耦合度,这其实也符合迪米特原则。
⨳ 提高可维护性: 将系统的复杂性封装在门面对象中,使系统更易于维护和修改。
门面模式缺点如下:
⨳ 不符合开闭原则: 如果系统需要新增功能,可能需要修改门面类,违反了开闭原则。
⨳ 可能引入性能问题: 如果门面对象负责过多的子系统交互,可能会引入性能问题,导致系统性能下降。
⨳ 隐藏系统复杂性: 虽然门面模式隐藏了系统的复杂性,但有时也可能导致客户端对系统整体的理解不足,造成误解或不当使用。
综上所述,门面模式在简化接口、降低耦合度等方面有着明显的优势,但也需要注意可能存在的性能问题和对系统整体的理解。
转载自:https://juejin.cn/post/7373986431850905637