名称
外观模式(FACADE)
目的
为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
适用性
在遇到以下情况使用Facade模式
要为一个复杂子系统提供一个简单接口时。子系统往往因为不断演化而变得越来越复杂。大多数模式使用时都会产生更多更小的类。这使得子系统更具可重用性,也更容易对子系统进行定制,但这也给那些不需要定制子系统的用户带来一些使用上的困难。Facade可以提供一个简单的缺省视图,这一视图对大多数用户来说已经足够,而那些需要更多的可定制性的用户可以越过facade层。
客户程序与抽象类的实现部分之间存在着很大的依赖性。引入facade将这个子系统与客户以及其他的子系统分离,可以提高子系统的独立性和可移植性。
当需要构建一个层次结构的子系统时,使用facade模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,可以让它们仅通过facade进行通讯,从而简化了它们之间的依赖关系
结构
外观(Facade):
知道哪些子系统类负责处理请求。
将客户的请求代理给适当的子系统对象
子系统类(Subsystem classes):
实现子系统的功能。
处理由Facade对象指派的任务。
没有facade的任何相关信息;即没有指向facade的指针。
协作
客户程序通过发送请求给Facade的方式与子系统通讯,Facade将这些消息转发给适当的子系统对象。尽管是子系统中的有关对象在做实际工作,但Facade模式本身也必须将它的接口转换成子系统的接口。
使用Facade的客户程序不需要直接访问子系统对象。
效果
优点
它对客户屏蔽子系统组件,因而减少了客户处理的对象的数目并使得子系统使用起来更加方便。
它实现了子系统与客户之间的松耦合关系,而子系统内部的功能组件往往是紧耦合的。松耦合关系使得子系统的组件变化不会影响到它的客户。
Facade模式有助于建立层次结构系统,也有助于对对象之间的依赖关系分层。Facade模式可以消除复杂的循环依赖关系。这一点在客户程序与子系统是分别实现的时候尤为重要。
如果应用需要,Facade并不限制它们使用子系统类。因此可以在系统易用性和通用性之间加以选择。
易于维护和扩展。当子系统发生变化时,只需要调整外观类的实现,而不必修改客户端代码。
提高灵活性。可以在不改变客户端代码的情况下,通过更改外观类(要有抽象外观)来改变子系统的具体实现。
缺点
违反开闭原则。如果没有引入抽象外观类,在添加新的子系统时可能需要修改外观类或客户端的源代码,这违背了“开闭原则”。
控制粒度问题。外观类可能会变得过于庞大,特别是当子系统很复杂时,这可能导致外观类难以维护。
限制客户端的灵活性。如果对外观类进行了过多的封装,可能会限制客户端直接访问子系统的能力,减少灵活性。
可能引入额外的开销。通过外观类调用子系统可能会引入额外的方法调用开销,尤其是在性能敏感的应用场景中。
应用
问题
用过小爱同学的小伙伴应该都知道它能控制家里的智能家电,但这又是怎么实现的呢?其实这里面就用到了外观模式的思想,通过小爱同学平台,整合所有的智能家电,通过MQTT对它们的物联网模块发送指令,物联网模块收到指令运行相应引脚的电路达到控制家电的目的。当然,作为平台,不可能一个个去对接不同品牌的电器,一般是各个品牌电器根据小爱同学提供的接口去对接各自的操作指令。这里我们模拟一下家用电器的控制流程,小爱同学作为平台(外观)提供了两个操作接口分别是on()和off()去控制电器的开关,空调、灯、电视分别对接了小爱同学(在小爱同学那里留下了自己的类引用)。用户创建了两条智能,智能“回家”内容为小爱同学执行on()的时候分别依次执行灯、空调、电视的on(),智能“出门”内容为小爱同学执行off()时依次执行电视、空调、灯的off(),通过人体传感器(以下代码中为客户端)进行判断执行。这样就实现了回家时开灯、开空调、开电视,出门时关电视、关空调、关灯。
示例
UML
代码示例
外观:小爱同学
package com.ysj.part4.facade.facade;
import com.ysj.part4.facade.subsystem.AirCondition;
import com.ysj.part4.facade.subsystem.Light;
import com.ysj.part4.facade.subsystem.Television;
/**
* 小爱同学
*/
public class XiaoaiCLassmate {
Light light;
AirCondition ac;
Television tv;
public XiaoaiCLassmate(Light light, AirCondition ac, Television tv){
this.light = light;
this.ac = ac;
this.tv = tv;
}
public void on(){
light.on(); //首先开灯
ac.on(); //然后是打开空调
tv.on(); //最后是打开电视
}
public void off(){
tv.off(); //首先关闭电视机
ac.off(); //空调关闭
light.off(); //最后关灯
}
}
子系统:空调、灯、电视
package com.ysj.part4.facade.subsystem;
public class AirCondition {
public void on(){
System.out.println("打开了空调....");
}
public void off(){
System.out.println("关闭了空调....");
}
}
package com.ysj.part4.facade.subsystem;
public class Light {
public void on(){
System.out.println("打开了电灯....");
}
public void off(){
System.out.println("关闭了电灯....");
}
}
package com.ysj.part4.facade.subsystem;
public class Television {
public void on(){
System.out.println("打开了电视....");
}
public void off(){
System.out.println("关闭了电视....");
}
}
客户端:人体传感器
package com.ysj.part4.facade;
import com.ysj.part4.facade.facade.XiaoaiCLassmate;
import com.ysj.part4.facade.subsystem.AirCondition;
import com.ysj.part4.facade.subsystem.Light;
import com.ysj.part4.facade.subsystem.Television;
public class Client {
public static void main(String[] args) {
//实例化子系统对象
Light light = new Light();
Television tv = new Television();
AirCondition ac = new AirCondition();
// 实例化小爱同学
XiaoaiCLassmate watchTv = new XiaoaiCLassmate(light, ac, tv);
// 调用小爱同学的方法从而调用间接调用子系统方法
System.out.println("--------------回家.........");
watchTv.on();
System.out.println("--------------出门...........");
watchTv.off();
}
}
已知应用
Spring Framework
Spring 的 IOC 容器可以被视为一种外观模式的应用,它简化了依赖管理和配置。
开发过程中的自建工具类
开发过程中,直接使用某些框架或工具并不是那么好用,很多重复的操作直接使用会很难看,这个时候就可以用工具类封装这部分重复操作,其他地方要使用这些框架或工具的时候直接调用工具类方法就行,比较常见的如封装redisTemplate(子系统)的操作形成RedisUtils(外观)。
评论区