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
运行时行为 执行子系统方法组合(如开启音响、投影仪、调暗灯光) 创建对象实例,客户端随后调用对象方法(如记录日志)
封装内容 封装子系统交互逻辑,隐藏协作细节 封装对象创建逻辑,隐藏实例化细节
扩展方式 添加新子系统操作需修改外观类或新增方法 添加新产品和对应工厂类,无需修改现有代码
客户端代码复杂度 极低,仅调用外观类方法 较低,需通过工厂获取对象后再调用方法
示例输出 按顺序调用子系统方法(如"音响已开启"、"投影仪已开启") 创建对象并调用其方法(如"文件日志记录: 消息")

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

相关推荐
数据智能老司机3 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
金銀銅鐵3 小时前
Spring 中的 initializeBean 方法的内部逻辑小总结
spring
数据智能老司机4 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机4 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机4 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
使一颗心免于哀伤4 小时前
《设计模式之禅》笔记摘录 - 21.状态模式
笔记·设计模式
数据智能老司机1 天前
精通 Python 设计模式——创建型设计模式
python·设计模式·架构
数据智能老司机1 天前
精通 Python 设计模式——SOLID 原则
python·设计模式·架构
烛阴1 天前
【TS 设计模式完全指南】懒加载、缓存与权限控制:代理模式在 TypeScript 中的三大妙用
javascript·设计模式·typescript
李广坤1 天前
工厂模式
设计模式