【设计模式】【结构型模式】外观模式(Facde)

简介: 一、入门 什么是外观模式? 一种结构型设计模式,通过为子系统中的一组接口提供一个统一的高层接口(称为外观),来简化客户端与复杂子系统的交互过程。其本质是建立抽象层来隔离复杂度。 为什么要有外观模式?

👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD

🔥 2025本人正在沉淀中... 博客更新速度++

👍 欢迎点赞、收藏、关注,跟上我的更新节奏

🎵 当你的天空突然下了大雨,那是我在为你炸乌云

一、入门

什么是外观模式?

一种结构型设计模式,通过为子系统中的一组接口提供一个统一的高层接口(称为外观),来简化客户端与复杂子系统的交互过程。其本质是建立抽象层来隔离复杂度。

为什么要有外观模式?

假设我们有一个包含多个设备的智能家居系统:

// 子系统类
class Light {
   
    void on() {
    System.out.println("开灯"); }
    void off() {
    System.out.println("关灯"); }
}

class AirConditioner {
   
    void startCooling() {
    System.out.println("开启制冷"); }
    void stop() {
    System.out.println("关闭空调"); }
}

class SoundSystem {
   
    void playMusic() {
    System.out.println("播放音乐"); }
    void stop() {
    System.out.println("关闭音响"); }
}
客户端调用
public class Client {
   
    public static void main(String[] args) {
   
        Light light = new Light();
        AirConditioner ac = new AirConditioner();
        SoundSystem ss = new SoundSystem();

        // 开启回家模式
        light.on();
        ac.startCooling();
        ss.playMusic();

        // 开启离家模式
        light.off();
        ac.stop();
        ss.stop();
    }
}

传统调用方式存在的问题

  1. 高耦合:客户端需要了解所有子系统细节
  2. 操作繁琐:每个模式都要逐个调用多个方法
  3. 维护困难:子系统变更会导致所有客户端修改
  4. 错误风险:容易遗漏步骤(比如忘记关空调)

如何实现外观模式?

  1. Facade(外观角色):系统的门户入口,知晓所有子系统功能,将客户端请求转发给对应子系统对象组合多个子系统操作形成高层接口。
  2. Subsystem Classes(子系统角色):实际功能的提供者,可以是独立存在的类/模块,通常不知道外观的存在(反向无依赖),内部可能继续包含更细粒度的子系统。
  3. Additional Facade(可选扩展角色):通过创建多个外观类,实现不同维度的接口封装

【案例】智能家电 - 改

image.png

Facade(外观角色):整合子系统操作,SmartHomeFacade类。

class SmartHomeFacade {
   
    // 持有全部子系统引用
    private Light light;
    private AirConditioner ac;
    private SoundSystem ss;

    public SmartHomeFacade() {
   
        this.light = new Light();     // 初始化子系统
        this.ac = new AirConditioner();
        this.ss = new SoundSystem();
    }

    // 封装组合操作(核心价值所在)
    public void leaveHome() {
   
        light.off();
        ac.stop();
        ss.stop();
    }
}

Subsystem Classes(子系统角色):由于LightAirConditionerSoundSystem类组成。

class Light {
    // 灯光子系统
    void on() {
    System.out.println("开灯"); }
    void off() {
    System.out.println("关灯"); }
}

class AirConditioner {
    // 空调子系统
    void startCooling() {
    System.out.println("开启制冷"); }
    void stop() {
    System.out.println("关闭空调"); }
}

class SoundSystem {
    // 音响子系统
    void playMusic() {
    System.out.println("播放音乐"); }
    void stop() {
    System.out.println("关闭音响"); }
}

这里例子没有体现出Additional Facade(可选扩展角色),下面补充下

Additional Facade 的定位与作用

  • 诞生背景:当系统过于庞大时,单个外观类可能变得臃肿
  • 核心价值:按业务维度拆分接口,实现更精细化的访问控制
  • 类比现实:就像酒店的总服务台(主外观)和 VIP 专属管家(扩展外观)

假设系统新增两类用户需求:

  1. 基础用户:只需离家/回家模式
  2. 高级用户:需要影院模式、安防模式等
  3. 管理员:需要设备诊断功能
class SmartHomeFacade {
   
    // 若将所有方法堆积在一个类中...
    void leaveHome() {
    ... }    // 基础功能
    void backHome() {
    ... }
    void cinemaMode() {
    ... }   // 高级功能
    void securityMode() {
    ... }
    void checkDeviceStatus() {
    ... } // 管理功能
}

存在问题

  • 违反单一职责原则
  • 不同用户被迫看到不需要的方法
  • 方法过多导致维护困难

引入Additional Facade解决方案

// ----------------------------
// 主外观:基础功能
// ----------------------------
class BasicFacade {
   
    private Light light = new Light();
    private AirConditioner ac = new AirConditioner();

    public void leaveHome() {
   
        light.off();
        ac.stop();
    }
}

// ----------------------------
// 扩展外观1:娱乐功能
// ----------------------------
class EntertainmentFacade {
   
    private SoundSystem ss = new SoundSystem();
    private Projector projector = new Projector();

    public void cinemaMode() {
   
        ss.setSurroundSound();
        projector.lowerScreen();
    }
}

// ----------------------------
// 扩展外观2:管理功能
// ----------------------------
class AdminFacade {
   
    private DeviceDiagnostic diagnostic = new DeviceDiagnostic();

    public String getSystemHealth() {
   
        return diagnostic.checkAllDevices();
    }
}

何时需要 Additional Facade?

  1. 主外观类超过 200 行代码(IDE警告)
  2. 需要为不同用户角色提供差异化的接口
  3. 子系统存在明显的功能分组特征(如支付相关、物流相关)
  4. 团队开发时多人频繁修改同一个外观类

二、外观模式在框架源码中的运用

Spring Framework 中的 JdbcTemplate

模式角色 对应实现类/接口 源码片段示例
Facade org.springframework.jdbc.core.JdbcTemplate 封装所有JDBC操作
Subsystem javax.sql.DataSource、 java.sql.Connection、java.sql.Statement 原生JDBC组件
Client 业务层的DAO类 调用jdbcTemplate.query() 等

源码简化版

// Facade角色:JdbcTemplate
public class JdbcTemplate {
   
    // 持有子系统引用(通过DataSource获取Connection)
    private DataSource dataSource;

    // 封装execute操作(典型外观方法)
    public <T> T execute(StatementCallback<T> action) {
   
        // 获取连接(子系统操作)
        Connection con = DataSourceUtils.getConnection(obtainDataSource());
        Statement stmt = null;
        try {
   
            stmt = con.createStatement();
            // 执行回调(子系统操作)
            return action.doInStatement(stmt);
        } finally {
   
            // 释放资源(子系统操作)
            JdbcUtils.closeStatement(stmt);
            DataSourceUtils.releaseConnection(con, getDataSource());
        }
    }
}
  • 复杂度隐藏:将15+个JDBC步骤封装为query()/update()等简洁方法
  • 异常统一:将SQLException转换为``DataAccessException
  • 资源管理:自动处理连接获取/释放

Java NIO 的 Files 工具类(JDK内置外观)

模式角色 JDK实现类 示例方法
Facade java.nio.file.Files readAllLines()、write()
Subsystem FileSystem、Path、FileChannel NIO底层组件
// Facade角色:Files类
public final class Files {
   
    // 封装文件读取(组合多个NIO操作)
    public static List<String> readAllLines(Path path) throws IOException {
   
        try (BufferedReader reader = newBufferedReader(path)) {
   
            List<String> result = new ArrayList<>();
            for (;;) {
   
                String line = reader.readLine();
                if (line == null) break;
                result.add(line);
            }
            return result;
        }
    }

    // 内部实现使用子系统
    private static BufferedReader newBufferedReader(Path path) throws IOException {
   
        Charset cs = StandardCharsets.UTF_8;
        CharsetDecoder decoder = cs.newDecoder();
        FileChannel ch = FileChannel.open(path, StandardOpenOption.READ);
        return new BufferedReader(Channels.newReader(ch, decoder, -1));
    }
}

三、总结

外观模式的优点

  1. 简化客户端调用:
    外观模式通过封装复杂的子系统调用,提供一个简单的高层接口。客户端不再需要了解子系统的内部细节,只需调用外观类的方法即可完成复杂操作。例如,Spring的JdbcTemplate封装了JDBC的繁琐操作,开发者只需调用query()update()方法,而无需关心连接管理、异常处理等细节。
  2. 降低耦合度:
    客户端只依赖外观类,而不直接依赖子系统。这种设计减少了模块间的耦合,使得系统更易于维护和扩展。例如,SLF4J日志门面允许开发者使用统一的日志接口,而无需关心底层是Logback还是Log4j。
  3. 提高可维护性:
    当子系统发生变化时,只需修改外观类的实现,而客户端代码无需调整。这种集中化的修改点大大降低了维护成本。例如,Java NIO的Files工具类封装了文件操作的复杂性,即使底层NIO实现发生变化,客户端代码也不需要修改。
  4. 增强灵活性:
    外观模式可以通过扩展多个外观类来提供不同维度的接口。例如,在智能家居系统中,可以为普通用户提供BasicFacade,为高级用户提供AdvancedFacade,满足不同场景的需求。
  5. 统一异常处理:
    外观类可以集中处理子系统的异常,并将其转换为更友好的错误信息。例如,Spring的JdbcTemplate将底层的SQLException转换为DataAccessException,使得异常处理更加一致和清晰。

外观模式的缺点

  1. 过度封装风险:
    如果外观类设计不当,可能会变成一个“上帝对象”,包含过多的职责,导致系统灵活性下降。例如,一个外观类如果封装了所有业务逻辑,可能会变得难以维护。
  2. 性能开销:
    外观模式引入了额外的调用层次,可能会带来轻微的性能损耗。在性能敏感的场景中,这种开销需要特别注意。例如,在高频交易系统中,直接调用底层库可能比通过外观类更高效。
  3. 滥用可能性:
    在简单场景中使用外观模式可能会导致过度设计,增加不必要的复杂性。例如,如果一个系统只有一两个简单的子系统,直接调用可能比引入外观模式更合适。
  4. 调试难度增加:
    由于外观类隐藏了子系统的细节,当出现问题时,调试可能需要额外关注外观层的逻辑。例如,如果外观类中的某个方法调用链较长,定位问题可能会更加困难。

适用场景

  1. 复杂子系统整合:
    当系统包含多个复杂的子系统,且需要为客户端提供一个统一的入口时,外观模式非常适用。例如,微服务架构中的API Gateway就是一个典型的外观模式应用,它整合了多个微服务的接口,为客户端提供统一的访问点。
  2. 分层架构设计:
    在分层架构中,外观模式可以用于在层与层之间建立清晰的访问边界。例如,在Spring应用中,Service层通常作为DAO层的外观,封装数据访问逻辑,为Controller层提供简洁的接口。
  3. 第三方SDK封装:
    当需要集成第三方SDK时,外观模式可以用于统一管理外部依赖的初始化、配置和调用。例如,支付SDK的封装(如支付宝、微信支付)通常会使用外观模式,隐藏复杂的支付流程,提供简单的支付接口。
  4. 遗留系统改造:
    当需要对旧系统进行现代化改造时,外观模式可以为旧系统提供新的接口,避免直接修改原有代码。例如,老旧ERP系统的REST API封装就是一个典型的外观模式应用。
  5. 多环境适配:
    当系统需要在不同环境(如测试、生产)中运行时,外观模式可以为不同环境提供一致的调用方式。例如,配置中心的客户端封装可以隐藏不同环境的配置细节,为应用提供统一的配置访问接口。
目录
相关文章
|
1月前
|
设计模式 存储 缓存
【设计模式】【结构型模式】享元模式(Flyweight)
一、入门 什么是享元模式? 享元模式(Flyweight Pattern)是一种结构型设计模式,旨在通过共享对象来减少内存使用,特别适用于存在大量相似对象的情况。 它的核心思想是将对象的内在状态(不变
80 16
|
1月前
|
设计模式 Java 数据库连接
【设计模式】【结构型模式】代理模式(Proxy)
一、入门 什么是代理模式? 代理模式(Proxy Pattern)是一种结构型设计模式,允许你提供一个代理对象来控制对另一个对象的访问。 代理对象在客户端和目标对象之间起到中介作用,可以在不改变目标对
63 10
|
1月前
|
设计模式 Java 定位技术
【设计模式】【结构型模式】组合模式(Composite)
一、入门 什么是组合模式 组合模式(Composite Pattern)是一种结构型设计模式,它允许你将对象组合成树形结构来表示“部分-整体”的层次关系。组合模式使得客户端可以统一处理单个对象和组合对
69 10
|
1月前
|
关系型数据库 Java MySQL
【设计模式】【结构型模式】桥接模式(Bridge)
一、入门 什么是桥接模式? 桥接模式(Bridge Pattern)是一种结构型设计模式,核心思想是将抽象与实现分离,让它们可以独立变化。简单来说,它像一座“桥”连接了两个维度的变化,避免用继承导致代
131 10
|
1月前
|
设计模式 前端开发 Java
【设计模式】【结构型模式】适配器模式(Adpter)
一、入门 什么是适配器模式? 适配器模式是Java中常用的结构型设计模式,它的核心作用就像现实中的电源转换器一样---让原本不兼容的两个接口能够协同工作。 为什么要用适配器模式? 假设我们需要在电商系
74 10
|
1月前
|
设计模式 缓存 安全
【设计模式】【结构型模式】装饰者模式(Decorator)
一、入门 什么是装饰者模式? 装饰者模式(Decorator Pattern)是 Java 中常用的结构型设计模式,它能在不修改原有对象结构的前提下,动态地为对象添加额外的职责。 为什么要装饰者模式?
47 8
|
5月前
|
设计模式 缓存 应用服务中间件
「全网最细 + 实战源码案例」设计模式——外观模式
外观模式(Facade Pattern)是一种结构型设计模式,旨在为复杂的子系统提供一个统一且简化的接口。通过封装多个子系统的复杂性,外观模式使外部调用更加简单、易用。例如,在智能家居系统中,外观类可以同时控制空调、灯光和电视的开关,而用户只需发出一个指令即可。
176 69
|
9月前
|
设计模式 Java
Java设计模式-外观模式(11)
Java设计模式-外观模式(11)
|
10月前
|
设计模式 存储 Java
【九】设计模式~~~结构型模式~~~外观模式(Java)
文章详细介绍了外观模式(Facade Pattern),这是一种对象结构型模式,通过引入一个外观类来简化客户端与多个子系统之间的交互,降低系统的耦合度,并提供一个统一的高层接口来使用子系统。通过文件加密模块的实例,展示了外观模式的动机、定义、结构、优点、缺点以及适用场景,并讨论了如何通过引入抽象外观类来提高系统的可扩展性。
【九】设计模式~~~结构型模式~~~外观模式(Java)
|
10月前
|
设计模式 缓存 Java
【十一】设计模式~~~结构型模式~~~代理模式(Java)
文章详细介绍了代理模式(Proxy Pattern),这是一种对象结构型模式,用于给对象提供一个代理以控制对它的访问。文中阐述了代理模式的动机、定义、结构、优点、缺点和适用环境,并探讨了远程代理、虚拟代理、保护代理等不同代理形式。通过一个商务信息查询系统的实例,展示了如何使用代理模式来增加身份验证和日志记录功能,同时保持客户端代码的无差别对待。此外,还讨论了代理模式在分布式技术和Spring AOP中的应用,以及动态代理的概念。
【十一】设计模式~~~结构型模式~~~代理模式(Java)