简介
外观模式隐藏了系统的复杂性,为子系统中的一组接口提供了一个统一的高层接口,客户端通过高层接口访问子系统,不需要了解子系统的内部细节,这样使子系统更容易使用。
实际开发中因为需求变动,可能导致系统趋于复杂,在使用某些类时可能需要了解类的详细细节,比如方法调动步骤以及调用顺序,使用不当则会导致错误。
外观模式用于解决类似问题,由外观模式提供高层级的简单接口,调用者只要请求外观接口就能得到准确结果,不需要调用者去处理类的内部复杂逻辑。
外观模式还适用于处理子系统之间调用问题,保持子系统的独立性,一般而言,子系统之间相互依赖、相互调用,外观模式可以整合子系统的接口形成统一接口,定义更高级的接口让子系统更容易使用,提高独立性、层次性。
模式角色
门面角色:被客户角色调用,提供对子系统接口的高层次访问;
子系统角色:提供实际功能,被外观角色调用,但是不会调用外观角色;
客户端角色:不关心系统内部复杂子系统的运作情况,而只与外观角色通信获取数据。
适用场景
软件系统复杂度较高,需要提供一个更高级别的简单接口简化对子系统的操作;
客户端与实现类间有太多依赖,需要降低耦合性;
多个子系统互相依赖,此时需要层级化子系统,简化子系统之间的依赖性。
示例
假设有三个子系统,当客户端执行某一业务时,需要涉及到这三个子系统,且包含调用顺序:
public class SubSystemA {
public void methodA(){
System.out.println("1. 子系统A执行方法A");
}
}
public class SubSystemB {
public void methodB(){
System.out.println("2. 子系统B执行方法B");
}
}
public class SubSystemC {
public void methodC(){
System.out.println("3. 子系统C执行方法C");
}
}
门面类:
public class Facade {
private SubSystemA a;
private SubSystemB b;
private SubSystemC c;
public Facade() {
a = new SubSystemA();
b = new SubSystemB();
c = new SubSystemC();
}
public void execute(){
a.methodA();
b.methodB();
c.methodC();
}
}
客户端角色:
public class Cilent {
public static void main(String[] args) {
Facade facade = new Facade();
facade.execute();
}
}
客户端执行业务时,不需要了解需要涉及几个子系统,也不需要了解子系统的调用顺序,这些对于客户端都是透明的。客户端只需要和门面通信就能获得自己想要的结果。
优点
松耦合
门面模式是符合迪米特法则(最少知识原则)的,迪米特法则强调了类和类之间应当保持松耦合关系,一个类应当尽可能少的与其他类发生相互作用,如果两个类不直接通信,那么这两个类就不应当发生直接的相互作用。如果一个类需要调用另一个类的某个方法,可以通过第三个类转发这个调用。
封装变化的部分
外观模式中,只要外观不发生变化,客户端就不会受任何影响,因为外观封装了对子系统中的变化,对于客户端是完全透明的。
jdk中的外观模式
java.util.logger.Logger类使用了外观模式,Logger类中包含了多个辅助类,客户端只需要通过调用Logger类,不需要了解其他多个辅助类的具体细节,从而实现了客户端和辅助类的解耦,简化了调用。
Logger:日志记录对象。用于记录日志信息。
Handler:用于处理日志信息的输出。在 Handler 类中,可以决定日志是输出到文件中还是控制台中(相当于log4j中的appender)。
Filter:用于过滤日志。在 Filter 类中,可以根据日志级别或者某种条件来决定是否输出该日志。这样达到去除冗余信息的目的。
Formatter:用于格式化日志信息。该类可以将日志文本格式化成 XML 或者 HTML 的格式,这完全依赖于具体的实现。
Level:用于表示日志的级别。 JDK 日志框架默认有如下级别:SEVERE(最高值) 、WARNING 、INFO 、CONFIG 、FINE 、FINER 、FINEST(最低值)、ALL(记录所有信息) OFF(不记录任何级别信息)。
和其他模式的关系
模板方法模式
模板方法模式中固定了方法的调用模板,而门面模式也有固定方法调用模板、次序的效果,但是模板方法模式一般固定的是本身的方法流程,且可以提供给子类重写。
组合模式
和组合模式一样,外观模式要实现对子系统的访问,一般情况下都需要将子系统角色组合到门面类中,从这个角度,和装饰模式、代理模式也有类似之处。
参考:《设计模式那点事》
https://www.alicharles.com/article/design-pattern/jdk-facade-pattern/