hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD
2025本人正在沉淀中… 博客更新速度++
欢迎点赞、收藏、关注,跟上我的更新节奏
当你的天空突然下了大雨,那是我在为你炸乌云
文章目录
- 一、入门
- 什么是外观模式?
- 为什么要有外观模式?
- 如何实现外观模式?
- 二、外观模式在框架源码中的运用
- Spring Framework 中的 JdbcTemplate
- Java NIO 的 Files 工具类(JDK内置外观)
- 三、总结
- 外观模式的优点
- 外观模式的缺点
- 适用场景
一种结构型设计模式,通过为子系统中的一组接口提供一个统一的高层接口(称为外观),来简化客户端与复杂子系统的交互过程。其本质是建立抽象层来隔离复杂度。
假设我们有一个包含多个设备的智能家居系统:
// 子系统类
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();
}
}
传统调用方式存在的问题:
【案例】智能家电 - 改
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(子系统角色):由于Light
、AirConditioner
、SoundSystem
类组成。
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 的定位与作用
假设系统新增两类用户需求:
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?
模式角色 | 对应实现类/接口 | 源码片段示例 |
---|---|---|
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());
}
}
}
query()
/update()
等简洁方法SQLException转换为``DataAccessException
模式角色 | 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));
}
}
JdbcTemplate
封装了JDBC的繁琐操作,开发者只需调用query()
或update()
方法,而无需关心连接管理、异常处理等细节。Files
工具类封装了文件操作的复杂性,即使底层NIO实现发生变化,客户端代码也不需要修改。BasicFacade
,为高级用户提供AdvancedFacade
,满足不同场景的需求。JdbcTemplate
将底层的SQLException
转换为DataAccessException
,使得异常处理更加一致和清晰。