外观模式
Facade Pattern:外观模式,是GoF23种设计模式之中属于结构型模式的一种。外观模式的定义就是为一组接口外面在包装一个一致的接口,通过这个一致的接口再去访问被包装的一组接口。
说起来可能是有点绕,但是可以把这个外观模式想象成黑盒,类比于熟悉的黑盒测试,当我们需要对一个模块进行功能测试的时候,使用黑盒测试,此时我们是不知道这个模块的具体实现的代码的,很有可能的是我们都得不到这个模块的代码,只有这个模块抛出来可以给我们统一调用的API接口而已。
这可以类比于外观模式的目的和定义,外观模式就是在将一组接口包装起来成一个接口,外界用户甚至都不知道这些接口,只能直接通过那一个统一的接口调用和返回自己所需要的信息。
结构
通过类图简单的分析一下:
- 外观类:这个是外观模式的中心,这个外观类里面包含着所有接口,知道哪些接口可以处理哪些请求,完成对应的服务,而且用户也是直接调用的这个类,用户是不知道被外观类包含的接口存在的。
- 功能接口(这只是一个描述而已,也可以是子系统类等等):这些是需要被包含的类的集合,当然可以是接口也可以是类,但是这些功能接口都是需要包含到外观类之中。
举例说明
-
外观模式的使用很多很多,一说起来就可以知道的就是前后端分离了。对于前后端完全分离的开发,前端是完全不知道后端在干什么的,后端也不会管前端在干什么,前端只需要调用后端给出的API接口,后端也只需要编写API接口,两者谁也不认识谁,谁也不需要了解谁,拿来就可以用。
-
再打个比方,比如我们自己买的笔记本电脑,我们收到笔记本电脑之后需要怎么使用?很简单,按下开机键,开机,就可以使用了。
至于里面的什么主板,什么CPU,什么显卡,什么散热器,什么SSD这些都不需要我们自己组装,也不需要我们制作,可以做的就是拿起来就用。
这个位置就可以形象的吧笔记本电脑当成一个外观类,而里面的所有其他东西都是被包装的功能接口的体现,不论是CPU,显卡的硬件还是Windows,Mac,Lunix系统的软件,这些是我们不知道或者不需要管理的。
-
说个高级点的例子,对于Spring Cloud全家桶的使用,搭建分布式微服务的应用就是这样的,每个微服务之间都是独立开发的,你不了解我,我不了解你,但是我可以使用你,对外都是一致的抛出API接口,对内则是每个微服务自己解决对方的请求并且给出答复,当然这个例子不是很恰当啊,仅当是听听就可以了。
注意
- 外观类,之前说过是需要包含所有子系统接口的,这就不符合开闭原则了,因为如果后期需要加东西,这是需要修改源代码的,这就破坏了开闭原则的定义规范了。
- 但是外观模式最为重要的一个点就是松耦合的特征,解决掉客户端和系统的耦合关系,客户端不需要按照自己的要求在系统中找到对应的实现接口,而是直接通过外观类,至于其他的事情就不该客户端来办了,只需要等待最后的结果就行了。
- 还有个好处就是提高了安全性,重要的,主要的接口都不需要对外暴露,直接封装起来,变成一个黑盒子,外部的用户调用只知道暴露出来的东西,其他的都不知道,算是加了一层防护保护。
一个小DEMO
-
场景
来个简单的Demo,这里我们有三个子系统,和一个包含三个子系统的外观类,可以叫他是接待员,负责收集用户请求和分发处理方式的作用的。
-
定义好我们的子系统
package com.facade; /** * 外观模式——需要被隐藏起来交给外观类托管的子系统A * @author WQ */ public class SubsystemA { public void show() { System.out.println("我是子系统A的处理方法!"); } } //------------------------------------------------------------ package com.facade; /** * 外观模式——需要被隐藏起来交给外观类托管的子系统B * @author WQ */ public class SubsystemB { public void show() { System.out.println("我是子系统B的处理方法!"); } } //------------------------------------------------------- package com.facade; /** * 外观模式——需要被隐藏起来交给外观类托管的子系统C * @author WQ */ public class SubsystemC { public void show() { System.out.println("我是子系统C的处理方法!"); } }
-
外观类,托管所有的子系统对象,并且提供外部访问的入口
package com.facade; /** * 外观模式——外观类 * 托管所有的子系统对象,并且提供给外界调用的入口 * @author WQ */ public class Facade { // 托管的对象 SubsystemA a; SubsystemB b; SubsystemC c; // 构造各个对象 public Facade() { a = new SubsystemA(); b = new SubsystemB(); c = new SubsystemC(); } //对外提供的方法调用入口 public void methodA() { a.show(); } public void methodB() { b.show(); } public void methodC() { c.show(); } public void methodABC() { a.show(); b.show(); c.show(); } }
-
客户端调用,也就是测试类了
package com.facade; /** * 外观模式——测试类 * @author WQ */ public class Main { public static void main(String[] args) { // 首先外观类用户是需要知道的 Facade facade = new Facade(); // 用户说:我要使用子系统A的功能 facade.methodA(); // 用户说:我要使用子系统B的功能 facade.methodB(); // 用户说:我要使用所有子系统的功能 facade.methodABC(); } }
-
测试走一波
我是子系统A的处理方法! 我是子系统B的处理方法! 我是子系统A的处理方法! 我是子系统B的处理方法! 我是子系统C的处理方法!
完成!!!