门面模式(外观模式)
1、什么是门面模式(外观模式)?
门面模式定义:门面模式提供一个高层次的接口, 使得子系统更易于使用。门面模式要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。通俗的说, 就是设计一个类,专门用于对外服务的, 门面对象是外界访问子系统内部的唯一通道。门面模式属于结构型模式,它向现有的系统中添加一个接口,来隐藏系统的复杂性。门面模式也称外观模式。
门面模式的类图示例如下:
其中两种角色如下:
1)Facade 门面角色: 客户端可以调用这个角色的方法. 此角色知晓子系统的所有功能和责任. 一般情况下, 本角色会将所有从客户端发来的请求委派到相应的子系统去, 也就是说该角色没有实际的业务逻辑, 只是一个委托类。
2)Subsystem 子系统角色(packagex): 可以同时有一个或多个子系统. 每一个子系统都不是一个单独的类, 而是一个类的集合,子系统并不知道门面的存在。 对于子系统而言, 门面仅仅是另外一个客户端而已。
2、门面模式的特性?
(1)意图:
为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
(2)主要解决:
降低访问复杂系统的内部子系统时的复杂度,简化客户端与之的接口。
(3)何时使用:
1)客户端不需要知道系统内部的复杂联系,整个系统只需提供一个"接待员"即可;
2)定义系统的入口。
(4)如何解决:
客户端不与系统耦合,外观类与系统耦合。
(5)关键代码:
在客户端和复杂系统之间再加一层,这一层将调用顺序、依赖关系等处理好。
3、门面模式的优缺点及应用场景
(1)门面模式的优点:
1)减少系统的相互依赖. 如果不使用门面模式, 外界访问直接深入到子系统内部, 相互之间是一种强耦合关系, 这样的强依赖是系统设计所不能接受的。门面模式的出项很好的解决了这个问题, 所有的依赖都是对门面对象的依赖, 与子系统无关。
2)提高了灵活性. 依赖减少了, 灵活性自然提高了。
3)提高安全性. 想让你访问子系统的哪些业务就开通那些逻辑, 不在门面上开通的方法, 休想访问到。
(2)门面模式的缺点:
门面模式最大的缺点就是不符合开闭原则, 对修改关闭, 对扩展开放. 一旦在系统投产后发现有一个小错误, 怎么解决? 完全最从开闭原则,根本没办法解决,唯一能做的一件事就是修改门面对象的代码, 这个风险相当大。
(3)门面模式的使用场景:
1)为一个复杂的模块或子系统提供一个供外界访问的接口;
2)子系统相对独立--外界对子系统的访问只要黑箱操作即可;
3)预防低水平人员带来的风险扩散。
(4)注意事项:
在层次化结构中,可以使用门面模式定义系统中每一层的入口。
4、门面模式的程序示例
(1)示例场景:
假设现在多个子系统,多个客户端,每个客户端都需要使用这些子系统的方法。(以3个子系统2个客户端为例),如果直接实现调用,这时每个客户端都需要创建这些子系统,再分别调用这些子系统的方法。
(2)示例场景原始代码示例:
子系统类如下:
//子系统1
class SubSystem1{
public void method1(){
System.out.println("SubSystem1.method1 executed. ");
}
}
//子系统2
class SubSystem2{
public void method2(){
System.out.println("Subsystem2.method2 executed.");
}
}
//子系统3
class SubSystem3{
public void method3(){
System.out.println("SubSystem3.method3 executed.");
}
}
客户端类如下:
//客户端1
class Client1{
SubSystem1 subSystem1 = new SubSystem1();
SubSystem2 subSystem2 = new SubSystem2();
SubSystem3 subSystem3 = new SubSystem3();
public void doSomething1(){
subSystem1.method1();
subSystem2.method2();
subSystem3.method3();
}
}
//客户端n
class Client2{
SubSystem1 subSystem1 = new SubSystem1();
SubSystem2 subSystem2 = new SubSystem2();
SubSystem3 subSystem3 = new SubSystem3();
public void doSomething2(){
subSystem1.method1();
subSystem2.method2();
subSystem3.method3();
}
}
程序调用示例如下:
public class FacadeTest {
public static void main(String[] args) {
Client1 client1 = new Client1();
Client2 client2 = new Client2();
client1.doSomething1();
client2.doSomething2();
}
}
程序输出如下:
SubSystem1.method1 executed.
Subsystem2.method2 executed.
SubSystem3.method3 executed.
SubSystem1.method1 executed.
Subsystem2.method2 executed.
SubSystem3.method3 executed.
(3)使用门面模式的场景代码示例:
我们可以通过使用门面模式/外观模式来进行简化代码,我们创建一个门面类,在门面类中创建这些子系统,然后在门面类的方法中据需分别调用这些子系统的方法。客户端在使用时,直接通过门面类,调用门面类的方法来进行即可,这样便可大大减少重复的代码。使用门面类来充当了接口,隐藏了系统的复杂性。外观模式定义了一个高层的接口。
3个子系统的类不变,我们增加一个门面类,如下:
//引入外观模式
class Facade{
SubSystem1 subSystem1 = new SubSystem1();
SubSystem2 subSystem2 = new SubSystem2();
SubSystem3 subSystem3 = new SubSystem3();
public void doSomethingFacade(){
subSystem1.method1();
subSystem2.method2();
subSystem3.method3();
}
}
修改客户端中的程序调用代码,如下:
//门面模式下客户端1
class FacadeClient1{
Facade facade = new Facade();
public void doSomething1(){
facade.doSomethingFacade();
}
}
//门面模式下客户端n
class FacadeClient2{
Facade facade = new Facade();
public void doSomething2(){
facade.doSomethingFacade();
}
}
测试示例:
public class FacadeTest {
public static void main(String[] args) {
FacadeClient1 client1 = new FacadeClient1();
FacadeClient2 client2 = new FacadeClient2();
client1.doSomething1();
client2.doSomething2();
}
}
结果输出示例:
SubSystem1.method1 executed.
Subsystem2.method2 executed.
SubSystem3.method3 executed.
SubSystem1.method1 executed.
Subsystem2.method2 executed.
SubSystem3.method3 executed.
可以看出,门面模式提供了一个高层次的结果,降低了客户端和子系统的耦合度。门面模式是一个很好的封装方法, 一个子系统比较复杂时, 比如算法或者业务比较复杂, 就可以封装出一个或多个门面出来, 项目的接口简单, 而且扩展性非常好. 对于一个较大的项目, 为了避免人员带来的风险, 也可以使用门面模式。
5、门面模式在JDK中的典型应用
(1)tomcat中的Request和Response,如org.apache.catalina.connector.RequestFacade就是典型应用,是HttpServletRequest的一个实现。源码定义如下:
/**
* Facade class that wraps a Coyote request object.
* All methods are delegated to the wrapped request.
*
* @author Craig R. McClanahan
* @author Remy Maucherat
*/
@SuppressWarnings("deprecation")
public class RequestFacade implements HttpServletRequest {
//......
/**
* Construct a wrapper for the specified request.
*
* @param request The request to be wrapped
*/
public RequestFacade(Request request) {
//构造方法,这里传入的是org.apache.coyote.Request类
this.request = request;
}
}
通过说明就可以看出,RequestFacade只是一个门面类,包装了一个Coyote请求对象。 所有的方法都被委托给包装的请求对象org.apache.coyote.Request类。org.apache.coyote.Request类是在tomcat内部使用的Request类。
本文源代码:
https://github.com/JianfuYang/2020-yjf-review/tree/master/src/designpatterns/facade
声明:本文部分内容整理来源于网络,仅做个人学习使用!侵删~
本文部分内容参考链接:
https://www.runoob.com/design-pattern/facade-pattern.html
https://www.cnblogs.com/hujingnb/p/10171576.html