Java – GoF设计模式详解10(外观模式)

十、外观模式

1,基本介绍

(1)外观模式(
Facade)又叫作门面模式,是一种通过为多个复杂的子系统提供一个统一的高层接口,从而使这些子系统更容易使用的模式。外观模式有助于将子系统与客户端分离,并降低子系统与客户端之间的耦合度。它还有助于提高子系统的独立性,因为它们可以被独立地更改和扩展。 (2)该模式中包含的角色及其职责如下:

  • 外观Facade):它是一个接口,定义了客户端可以访问子系统的方法。
  • 子系统Subsystem):它是实现系统功能的类的集合。它对客户端是不可见的。
  • 客户端Client):它是使用子系统的类。


2,使用样例

(1)这里以汽车的启动为例演示外观模式的使用。用户只需按下启动按钮,后台就会自动完成引擎启动、仪表盘启动、车辆自检等过程。而按下停止按钮,则后台自动完成一系列装置的关闭过程。对于用户来说,他只需按下按钮即可,不用太关心具体的细节。首先我们定义引擎、仪表盘、自检器这三个类,并且其内部都有各自的启动、停止方法:

//引擎
public class Engine {
  //启动引擎
  public void startup() {
    System.out.println("引擎启动...");
  }
  //关闭引擎
  public void shutdown() {
    System.out.println("引擎关闭...");
  }
}

//仪表盘
public class Dashboard {
  //启动仪表盘
  public void startup() {
    System.out.println("仪表盘启动...");
  }
  //关闭仪表盘
  public void shutdown() {
    System.out.println("仪表盘关闭...");
  }
}

//自检器
public class SelfCheck {
  //启动自检器
  public void startup() {
    System.out.println("自检器启动...");
  }
  //关闭自检器
  public void shutdown() {
    System.out.println("自检器关闭...");
  }
}

(2)接着定义一个门面类,其内部会调用各个装置的启动、停止方法:

//启动装置
public class Starter {
  private Engine engine;
  private Dashboard dashboard;
  private SelfCheck selfCheck;

  public Starter() {
    this.engine = new Engine();
    this.dashboard = new Dashboard();
    this.selfCheck = new SelfCheck();
  }

  // 启动汽车
  public void startup() {
    System.out.println("--- 开始启动汽车 ---");
    this.engine.startup();
    this.dashboard.startup();
    this.selfCheck.startup();
    System.out.println("汽车启动完毕!");
  }

  // 停止汽车
  public void shutdown() {
    System.out.println("--- 开始停止汽车 ---");
    this.selfCheck.shutdown();
    this.dashboard.shutdown();
    this.engine.shutdown();
    System.out.println("汽车停止完毕!");
  }
}

(3)最后测试一下:

public class Test {
  @SneakyThrows
  public static void main(String[] args) {
    Starter starter = new Starter();
    starter.startup();
    starter.shutdown();
  }
}


附:SLF4J 中的外观模式

(1)
Java 原生的日志框架是
Java 日志系统(
JUL、全称
Java Util Logging)它提供了一组用于记录应用程序日志信息的工具。这个日志系统并没有直接使用外观模式,但是可以被用来实现外观模式。
JDK 日志框架主要包括如下几个部件:

  • Logger:日志记录对象。用于记录日志信息。
  • Handler:用于处理日志信息的输出。在 Handler 类中,可以决定日志是输出到文件中还是控制台中(相当于 log4j 中的 appender)。
  • Filter:用于过滤日志。在 Filter 类中,可以根据日志级别或者某种条件来决定是否输出该日志。这样达到去除冗余信息的目的。
  • Formatter:用于格式化日志信息。该类可以将日志文本格式化成 XML 或者 HTML 的格式,这完全依赖于具体的实现。
  • Level:用于表示日志的级别。 JDK 日志框架默认有如下级别:SEVERE(最高值)、WARNINGINFOCONFIGFINEFINERFINEST(最低值)、ALL(记录所有信息)、OFF(不记录任何级别信息)。


(2)
SLF4J
Simple Logging Facade for Java)即
Java 简单日志门面,是通过门面模式来实现,在项目代码中通过调用统一的
SLF4J 接口来调用日志相关功能,而不需要在写代码时关心日志实现的具体框架。

  • SLF4J 只是接口,不包含具体的实现
  • SLF4J 支持的日志实现框架有 Log4jJULjava.util.logging)、Log4j2Logback
  • SLF4J 的作者就是 Log4jLogback 的作者 Ceki Gülcü,他宣称 SLF4JLog4j 更有效率,而且比 Apache Commons LoggingJCL)简单、稳定。

(3)整个日志系统中,
SLF4J 就是门面,而下面的多种日志实现框架的适配层和底层实现就是我们在门面模式中所说的子系统。由于不需要在写代码时关心日志实现的具体框架,因此当底层的日志实现框架发生改变时代码不需要修改。这就是解除了应用和日志框架之间的耦合。

@RestController
public class HelloController {
 
    Logger logger = LoggerFactory.getLogger(getClass());
 
    @GetMapping("/test")
    public void test(){
        logger.trace("Trace 日志...");
        logger.debug("Debug 日志...");
        logger.info("Info 日志...");
        logger.warn("Warn 日志...");
        logger.error("Error 日志...");
    }
}

(4)如果项目有使用
Lombok 的话,直接使用
@Slf4j 注解可以省去从日志工厂生成日志对象这一步,直接进行日志记录。下面代码的效果同上面是一样的:

@RestController
@Slf4j
public class HelloController {
 
    @GetMapping("/test")
    public void test(){
        log.trace("Trace 日志...");
        log.debug("Debug 日志...");
        log.info("Info 日志...");
        log.warn("Warn 日志...");
        log.error("Error 日志...");
    }
}
THE END