Spring + 设计模式 (八) 结构型 - 外观模式

外观模式

引言

外观模式(Facade Pattern)是一种结构型设计模式,核心在于提供一个简化的接口,隐藏复杂子系统的内部细节,为客户端提供统一入口。它如同系统的"门面",将繁琐的操作封装成简单调用,降低使用成本。外观模式强调易用性和解耦,特别适合整合复杂模块或第三方库,让开发者专注于业务逻辑而非底层实现。

实际开发中的用途

在实际开发中,外观模式常用于简化复杂系统的访问,如集成第三方API、数据库操作或多模块协作。它解决了直接调用子系统带来的高耦合和学习成本问题,客户端只需通过外观接口操作,无需了解内部实现。例如,在支付系统中,外观模式可将多种支付方式(如微信、支付宝)的复杂流程封装为单一接口,提升代码可维护性和开发效率,特别适合企业级应用的模块化设计。

开发中的示例

设想一个电商系统的库存管理模块,涉及商品查询、库存更新和日志记录等子系统。若客户端直接调用这些子系统,代码将复杂且易出错。通过外观模式,可定义一个库存管理外观类,提供简单的 checkAndUpdateStock 方法,内部协调商品查询、库存扣减和日志记录。客户端只需调用外观接口,即可完成复杂操作,代码简洁且易于维护。

Spring 源码中的应用

Spring 框架中,外观模式在 JdbcTemplate 的实现中体现得淋漓尽致。JdbcTemplate 作为数据库操作的门面,封装了底层的 JDBC 操作(如连接管理、语句执行、异常处理),为开发者提供简洁的 API,屏蔽了 JDBC 的复杂性。

以下是 Spring 源码的典型片段(JdbcTemplate.java):

java 复制代码
// Spring 框架中的 JdbcTemplate
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
    private boolean lazyInit = true;

    public JdbcTemplate(DataSource dataSource) {
        setDataSource(dataSource);
        afterPropertiesSet();
    }

    @Override
    @Nullable
    public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action) throws DataAccessException {
        Assert.notNull(psc, "PreparedStatementCreator must not be null");
        Assert.notNull(action, "PreparedStatementCallback must not be null");
        if (logger.isDebugEnabled()) {
            logger.debug("Executing prepared SQL statement");
        }

        Connection con = DataSourceUtils.getConnection(obtainDataSource());
        PreparedStatement ps = null;
        try {
            ps = psc.createPreparedStatement(con);
            ResultSet rs = null;
            T result = action.doInPreparedStatement(ps);
            handleWarnings(ps);
            return result;
        } catch (SQLException ex) {
            throw translateSQLException("PreparedStatementCallback", getSql(psc), ex);
        } finally {
            JdbcUtils.closeStatement(ps);
            DataSourceUtils.releaseConnection(con, getDataSource());
        }
    }

    @Override
    public int update(String sql, Object... args) throws DataAccessException {
        return update(new SimplePreparedStatementCreator(sql), newArgPreparedStatementSetter(args));
    }
}

在这段代码中,JdbcTemplate 扮演外观模式的"门面"角色,简化了 JDBC 操作的复杂性。详细分析如下:

  1. 外观模式的体现
    • JdbcTemplate 封装了 JDBC 的核心操作(如获取连接、创建语句、处理结果集和异常),通过简单方法(如 executeupdate)对外提供统一接口。
    • 客户端无需直接操作 ConnectionPreparedStatement,仅需调用 JdbcTemplate 的方法即可完成数据库操作,符合外观模式的简化接口目标。
  2. 关键方法解析
    • execute 方法协调了连接获取、语句创建、异常处理和资源释放,隐藏了 JDBC 的底层细节。
    • update 方法进一步简化了参数绑定和 SQL 执行,客户端只需传入 SQL 和参数即可完成更新操作。
  3. 解耦与扩展性
    • 外观模式解耦了客户端与 JDBC 底层实现,客户端代码不依赖具体的数据库驱动,提升了可移植性。
    • JdbcTemplate 通过 DataSource 抽象数据库连接,支持无缝切换数据库(如 MySQL 到 PostgreSQL)。
  4. 实际问题解决
    • 它解决了 JDBC 操作的繁琐性和易错性(如资源泄漏、异常处理),通过统一的异常体系(DataAccessException)简化错误管理。
    • 提供批量操作、事务支持等功能,满足企业级应用的复杂需求。

这种实现使 JdbcTemplate 成为 Spring 数据访问层的核心组件,广泛应用于数据库操作,显著降低了开发者的学习和使用成本。

总结

外观模式如同一扇通往复杂系统的"简易之门",以统一接口屏蔽繁琐细节,让开发者如鱼得水。在 Spring 中,JdbcTemplate 通过外观模式简化了 JDBC 操作,赋予数据访问层的优雅与高效。结合 Spring Boot,开发者可轻松整合复杂模块,打造高内聚的系统。掌握外观模式,不仅能写出简洁的代码,更能设计出如 Spring 般用户友好的架构,让复杂性在门面之后悄然化解。

下面我在整理文章时觉得走马灯了,但是对比完我发现这就是两个东西,如果您觉的没必要对比就看我下面这句话后就可以退出了

外观模式是接口的组合,对外提供一个统一的调用;工厂模式就是接口的不同实现,对外提供不同的对象;其核心就是创建型模式和结构性模式的区别。

(看到这里 && 对您有帮助 && 觉得我总结的还行) -> 受累点个免费的赞👍,谢谢

外观模式和工厂模式对比

以下通过 Java 代码示例,对外观模式(Facade Pattern)和工厂模式(Factory Pattern)进行对比,展示它们的实现方式和差异。代码将聚焦于核心结构,省略不必要的复杂逻辑,并以注释说明关键点。最后通过表格总结代码中的对比点。

外观模式代码示例

外观模式通过一个外观类简化对多个子系统的访问。以下是一个模拟家庭影院系统的示例,外观类整合音响、投影仪和灯光的操作。

java 复制代码
// 子系统1:音响
class SoundSystem {
    void turnOn() {
        System.out.println("音响已开启");
    }
    void setVolume(int level) {
        System.out.println("音响音量设置为 " + level);
    }
    void turnOff() {
        System.out.println("音响已关闭");
    }
}

// 子系统2:投影仪
class Projector {
    void turnOn() {
        System.out.println("投影仪已开启");
    }
    void setInput(String input) {
        System.out.println("投影仪输入源设置为 " + input);
    }
    void turnOff() {
        System.out.println("投影仪已关闭");
    }
}

// 子系统3:灯光
class Light {
    void dim(int level) {
        System.out.println("灯光亮度调至 " + level);
    }
    void turnOff() {
        System.out.println("灯光已关闭");
    }
}

// 外观类:家庭影院外观
class HomeTheaterFacade {
    private SoundSystem soundSystem;
    private Projector projector;
    private Light light;

    public HomeTheaterFacade(SoundSystem soundSystem, Projector projector, Light light) {
        this.soundSystem = soundSystem;
        this.projector = projector;
        this.light = light;
    }

    // 简化操作:一键观看电影
    public void watchMovie() {
        System.out.println("准备观看电影...");
        light.dim(10);
        projector.turnOn();
        projector.setInput("HDMI");
        soundSystem.turnOn();
        soundSystem.setVolume(5);
    }

    // 简化操作:一键关闭
    public void endMovie() {
        System.out.println("关闭家庭影院...");
        soundSystem.turnOff();
        projector.turnOff();
        light.turnOff();
    }
}

// 客户端代码
public class FacadeDemo {
    public static void main(String[] args) {
        SoundSystem sound = new SoundSystem();
        Projector projector = new Projector();
        Light light = new Light();

        HomeTheaterFacade homeTheater = new HomeTheaterFacade(sound, projector, light);
        homeTheater.watchMovie();
        homeTheater.endMovie();
    }
}

代码说明

  • 外观类 HomeTheaterFacade 封装了对 SoundSystemProjectorLight 的复杂操作。
  • 客户端只需调用 watchMovie()endMovie(),无需直接与子系统交互。
  • 重点是协调多个子系统,提供简化的接口。

工厂模式代码示例

工厂模式通过工厂类封装对象创建逻辑。以下是一个模拟不同类型日志记录器的示例,工厂根据需求创建不同日志记录器。

java 复制代码
// 产品接口:日志记录器
interface Logger {
    void log(String message);
}

// 具体产品1:文件日志记录器
class FileLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println("文件日志记录: " + message);
    }
}

// 具体产品2:数据库日志记录器
class DatabaseLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println("数据库日志记录: " + message);
    }
}

// 工厂接口
interface LoggerFactory {
    Logger createLogger();
}

// 具体工厂1:文件日志工厂
class FileLoggerFactory implements LoggerFactory {
    @Override
    public Logger createLogger() {
        return new FileLogger();
    }
}

// 具体工厂2:数据库日志工厂
class DatabaseLoggerFactory implements LoggerFactory {
    @Override
    public Logger createLogger() {
        return new DatabaseLogger();
    }
}

// 客户端代码
public class FactoryDemo {
    public static void main(String[] args) {
        // 使用文件日志工厂
        LoggerFactory fileFactory = new FileLoggerFactory();
        Logger fileLogger = fileFactory.createLogger();
        fileLogger.log("这是一个文件日志消息");

        // 使用数据库日志工厂
        LoggerFactory dbFactory = new DatabaseLoggerFactory();
        Logger dbLogger = dbFactory.createLogger();
        dbLogger.log("这是一个数据库日志消息");
    }
}

代码说明

  • 工厂接口 LoggerFactory 和具体工厂类(FileLoggerFactoryDatabaseLoggerFactory)封装了日志记录器的创建。
  • 客户端通过工厂创建不同类型的 Logger 对象,无需直接实例化具体类。
  • 重点是动态创建对象,隔离创建逻辑。

代码对比分析

相同点
  1. 封装性:两者都通过中间层(外观类或工厂类)隐藏内部实现,客户端只需与高层接口交互。
  2. 简化客户端代码:外观模式通过外观类简化子系统调用,工厂模式通过工厂类简化对象创建。
  3. 解耦:客户端与具体实现(子系统或产品类)解耦,降低依赖性。
  4. 可扩展:两者都支持扩展(外观模式可添加新子系统操作,工厂模式可添加新产品和工厂)。
差异点
  1. 目的
    • 外观模式:协调多个子系统,提供统一的操作接口。
    • 工厂模式:创建不同类型的对象,管理实例化逻辑。
  2. 核心类作用
    • 外观类:封装子系统方法调用,执行组合操作。
    • 工厂类:负责实例化具体产品对象。
  3. 客户端交互
    • 外观模式:客户端调用外观类的方法,触发子系统协作。
    • 工厂模式:客户端通过工厂获取产品对象,再调用产品方法。
  4. 结构
    • 外观模式:一个外观类协调多个子系统类。
    • 工厂模式:一个工厂接口/类,多个具体工厂和产品类。
  5. 运行时行为
    • 外观模式:运行时执行子系统方法的组合。
    • 工厂模式:运行时创建对象,客户端随后使用对象。
对比项 外观模式 工厂模式
代码目的 协调多个子系统,提供简化的操作接口(如 watchMovie 创建不同类型的对象,封装实例化逻辑(如 createLogger
核心类 外观类(HomeTheaterFacade)协调子系统操作 工厂类(FileLoggerFactoryDatabaseLoggerFactory)创建产品对象
客户端交互 调用外观类方法(如 watchMovie()),触发子系统协作 调用工厂方法(如 createLogger()),获取产品后使用(如 log()
结构 外观类 + 多个子系统类(如 SoundSystemProjector 工厂接口 + 具体工厂类 + 产品接口 + 具体产品类(如 LoggerFileLogger
运行时行为 执行子系统方法组合(如开启音响、投影仪、调暗灯光) 创建对象实例,客户端随后调用对象方法(如记录日志)
封装内容 封装子系统交互逻辑,隐藏协作细节 封装对象创建逻辑,隐藏实例化细节
扩展方式 添加新子系统操作需修改外观类或新增方法 添加新产品和对应工厂类,无需修改现有代码
客户端代码复杂度 极低,仅调用外观类方法 较低,需通过工厂获取对象后再调用方法
示例输出 按顺序调用子系统方法(如"音响已开启"、"投影仪已开启") 创建对象并调用其方法(如"文件日志记录: 消息")

(对您有帮助 && 觉得我总结的还行) -> 受累点个免费的赞👍,谢谢

相关推荐
hello_ejb34 分钟前
聊聊Spring AI Alibaba的FeiShuDocumentReader
人工智能·python·spring
一只鹿鹿鹿1 小时前
【测试文档】项目测试文档,测试管理规程,测试计划,测试文档模版,软件测试报告书(Word)
数据库·后端·spring·单元测试
千千寰宇1 小时前
[设计模式/Java] 设计模式之门面模式(外观模式)【20】
设计模式
jstart千语1 小时前
【SpringBoot】HttpServletRequest获取使用及失效问题(包含@Async异步执行方案)
java·前端·spring boot·后端·spring
王有品2 小时前
Spring MVC 核心注解与文件上传教程
java·spring·mvc
ApeAssistant3 小时前
Spring + 设计模式 (七) 结构型 - 装饰器模式
spring·设计模式
Sc Turing3 小时前
Spring中的AOP基础理解
java·spring
都叫我大帅哥3 小时前
代码界的「万能前台」:门面模式的调停艺术
java·后端·设计模式