外观模式
引言
外观模式(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 操作的复杂性。详细分析如下:
- 外观模式的体现 :
JdbcTemplate
封装了 JDBC 的核心操作(如获取连接、创建语句、处理结果集和异常),通过简单方法(如execute
、update
)对外提供统一接口。- 客户端无需直接操作
Connection
或PreparedStatement
,仅需调用JdbcTemplate
的方法即可完成数据库操作,符合外观模式的简化接口目标。
- 关键方法解析 :
execute
方法协调了连接获取、语句创建、异常处理和资源释放,隐藏了 JDBC 的底层细节。update
方法进一步简化了参数绑定和 SQL 执行,客户端只需传入 SQL 和参数即可完成更新操作。
- 解耦与扩展性 :
- 外观模式解耦了客户端与 JDBC 底层实现,客户端代码不依赖具体的数据库驱动,提升了可移植性。
JdbcTemplate
通过DataSource
抽象数据库连接,支持无缝切换数据库(如 MySQL 到 PostgreSQL)。
- 实际问题解决 :
- 它解决了 JDBC 操作的繁琐性和易错性(如资源泄漏、异常处理),通过统一的异常体系(
DataAccessException
)简化错误管理。 - 提供批量操作、事务支持等功能,满足企业级应用的复杂需求。
- 它解决了 JDBC 操作的繁琐性和易错性(如资源泄漏、异常处理),通过统一的异常体系(
这种实现使 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
封装了对SoundSystem
、Projector
和Light
的复杂操作。 - 客户端只需调用
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
和具体工厂类(FileLoggerFactory
、DatabaseLoggerFactory
)封装了日志记录器的创建。 - 客户端通过工厂创建不同类型的
Logger
对象,无需直接实例化具体类。 - 重点是动态创建对象,隔离创建逻辑。
代码对比分析
相同点
- 封装性:两者都通过中间层(外观类或工厂类)隐藏内部实现,客户端只需与高层接口交互。
- 简化客户端代码:外观模式通过外观类简化子系统调用,工厂模式通过工厂类简化对象创建。
- 解耦:客户端与具体实现(子系统或产品类)解耦,降低依赖性。
- 可扩展:两者都支持扩展(外观模式可添加新子系统操作,工厂模式可添加新产品和工厂)。
差异点
- 目的 :
- 外观模式:协调多个子系统,提供统一的操作接口。
- 工厂模式:创建不同类型的对象,管理实例化逻辑。
- 核心类作用 :
- 外观类:封装子系统方法调用,执行组合操作。
- 工厂类:负责实例化具体产品对象。
- 客户端交互 :
- 外观模式:客户端调用外观类的方法,触发子系统协作。
- 工厂模式:客户端通过工厂获取产品对象,再调用产品方法。
- 结构 :
- 外观模式:一个外观类协调多个子系统类。
- 工厂模式:一个工厂接口/类,多个具体工厂和产品类。
- 运行时行为 :
- 外观模式:运行时执行子系统方法的组合。
- 工厂模式:运行时创建对象,客户端随后使用对象。
对比项 | 外观模式 | 工厂模式 |
---|---|---|
代码目的 | 协调多个子系统,提供简化的操作接口(如 watchMovie ) |
创建不同类型的对象,封装实例化逻辑(如 createLogger ) |
核心类 | 外观类(HomeTheaterFacade )协调子系统操作 |
工厂类(FileLoggerFactory 、DatabaseLoggerFactory )创建产品对象 |
客户端交互 | 调用外观类方法(如 watchMovie() ),触发子系统协作 |
调用工厂方法(如 createLogger() ),获取产品后使用(如 log() ) |
结构 | 外观类 + 多个子系统类(如 SoundSystem 、Projector ) |
工厂接口 + 具体工厂类 + 产品接口 + 具体产品类(如 Logger 、FileLogger ) |
运行时行为 | 执行子系统方法组合(如开启音响、投影仪、调暗灯光) | 创建对象实例,客户端随后调用对象方法(如记录日志) |
封装内容 | 封装子系统交互逻辑,隐藏协作细节 | 封装对象创建逻辑,隐藏实例化细节 |
扩展方式 | 添加新子系统操作需修改外观类或新增方法 | 添加新产品和对应工厂类,无需修改现有代码 |
客户端代码复杂度 | 极低,仅调用外观类方法 | 较低,需通过工厂获取对象后再调用方法 |
示例输出 | 按顺序调用子系统方法(如"音响已开启"、"投影仪已开启") | 创建对象并调用其方法(如"文件日志记录: 消息") |
(对您有帮助 && 觉得我总结的还行) -> 受累点个免费的赞👍,谢谢