外观模式也叫门面模式(Facade Pattern):外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。门面模式又称为外观模式,它是一种对象结构型模式。
简单来说就是隐藏了系统的复杂性,并向客户端提供了一个可以访问系统的接口,让客户可以更简单的使用这个系统。
这个模式中,一般存在3个角色。
- 门面角色:外观模式的核心。它被客户角色调用,它熟悉子系统的功能。内部根据客户角色的需求预定了几种功能的组合。(客户调用,同时自身调用子系统功能)
- 子系统角色: 实现了子系统的功能。它对客户角色和Facade时未知的。它内部可以有系统内的相互交互,也可以由供外界调用的接口。(实现具体功能)
- 客户角色: 通过调用Facede来完成要实现的功能(调用门面角色)。
=========================================================================
我们来模拟一个小爱同学进行米家集成控制的场景:
我们假设家里有三个米家设备,风扇,灯和电视,也就是门面角色:
风扇:
package FacadePattern.MiHomeProducts;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName MiFan.java
* @Description 小米风扇
* @createTime 2022年03月04日 14:49:00
*/
public class MiFan {
public void turnOn(){
System.out.println("风扇打开了");
}
public void turnOff(){
System.out.println("风扇关闭了");
}
}
电灯:
package FacadePattern.MiHomeProducts;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName MiLight.java
* @Description 小米电灯
* @createTime 2022年03月04日 14:49:00
*/
public class MiLight {
public void turnOn(){
System.out.println("电灯打开了");
}
public void turnOff(){
System.out.println("电灯关闭了");
}
}
电视:
package FacadePattern.MiHomeProducts;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName MiTv.java
* @Description 小米电视
* @createTime 2022年03月04日 14:48:00
*/
public class MiTv {
public void turnOn(){
System.out.println("电视打开了");
}
public void turnOff(){
System.out.println("电视关闭了");
}
}
如果我们回家要一个一个的打开,就会很麻烦:
模拟客户角色测试一下:
package FacadePattern;
import FacadePattern.MiHomeProducts.MiFan;
import FacadePattern.MiHomeProducts.MiLight;
import FacadePattern.MiHomeProducts.MiTv;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName FacedPatternTest.java
* @Description FacadePatternTest
* @createTime 2022年03月04日 15:02:00
*/
public class FacadePatternTest {
public static void main(String[] args) {
MiFan miFan = new MiFan();
MiTv miTv = new MiTv();
MiLight miLight = new MiLight();
miFan.turnOn();
miLight.turnOn();
miTv.turnOn();
}
}
可以看到,我们需要一个个去新建对象,然后执行方法,假设我们有n个设备,对设备进行m种操作,我们就需要新建n个对象,调用m次方法,这对于客户角色是非常不友好的。
那么我们要是使用小爱同学,把功能集成到小爱同学里面去,就会方便很多:
门面角色小爱:
package FacadePattern;
import FacadePattern.MiHomeProducts.MiFan;
import FacadePattern.MiHomeProducts.MiLight;
import FacadePattern.MiHomeProducts.MiTv;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName MiAi.java
* @Description 小爱同学
* @createTime 2022年03月04日 14:54:00
*/
public class MiAi {
private MiLight miLight;
private MiTv miTv;
private MiFan miFan;
public MiAi(){
miLight = new MiLight();
miTv = new MiTv();
miFan = new MiFan();
}
public void open(String common){
if ("我回家了".equals(common)) {
miFan.turnOn();
miLight.turnOn();
miTv.turnOn();
}else {
System.out.println("我还不会这项技能");
}
}
}
我们把创建对象和调用功能集成到了小爱的open方法中,对客户角色暴露的接口只有open,所以客户角色只需要调用他就可以了
再测一下:
package FacadePattern;
import FacadePattern.MiHomeProducts.MiFan;
import FacadePattern.MiHomeProducts.MiLight;
import FacadePattern.MiHomeProducts.MiTv;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName FacedPatternTest.java
* @Description FacadePatternTest
* @createTime 2022年03月04日 15:02:00
*/
public class FacadePatternTest {
public static void main(String[] args) {
MiFan miFan = new MiFan();
MiTv miTv = new MiTv();
MiLight miLight = new MiLight();
miFan.turnOn();
miLight.turnOn();
miTv.turnOn();
System.out.println("\n+++++++小爱调用++++++++\n");
MiAi miAi = new MiAi();
miAi.open("我回家了");
}
}
可以看到,一般我们遇到一个复杂子系统的调用的时候,我们可以使用外观模式来提供一个接口来降低用户使用的复杂度,并且可以在层次化结构中,使用外观模式降低层与层之间的耦合度。
外观模式最大的缺点在于违背了“开闭原则”,当增加新的子系统或者移除子系统时需要修改外观类,可以通过引入抽象外观类在一定程度上解决该问题,客户端针对抽象外观类进行编程。对于新的业务需求,不修改原有外观类,而对应增加一个新的具体外观类,由新的具体外观类来关联新的子系统对象,同时通过修改配置文件来达到不修改源代码并更换外观类的目的。