文章目录
-
- 前言
- [1. 外观模式是什么?](#1. 外观模式是什么?)
- [2. 外观模式能解决什么问题?](#2. 外观模式能解决什么问题?)
- [3. 实现思路](#3. 实现思路)
- [4. 示例](#4. 示例)
-
- [4.1 子系统(多个复杂模块)](#4.1 子系统(多个复杂模块))
- [4.2 外观类(Facade:统一入口)](#4.2 外观类(Facade:统一入口))
- [4.3 客户端调用(不需要关心内部细节)](#4.3 客户端调用(不需要关心内部细节))
- [5. 外观模式的典型结构](#5. 外观模式的典型结构)
- [6. 优缺点](#6. 优缺点)
-
- [6.1 优点](#6.1 优点)
- [6.2 缺点](#6.2 缺点)
- [7. 外观模式与代理模式/装饰器模式的区别](#7. 外观模式与代理模式/装饰器模式的区别)
- [8. 适用场景](#8. 适用场景)
- [9. 总结](#9. 总结)
前言
在软件开发中,很多系统都会暴露出"很复杂的子系统调用方式":
比如要先创建多个对象、按顺序执行多步流程、还要处理很多细节参数。
如果客户端每次都要直接面对这些细节,就会导致:
- 调用方代码臃肿、难维护
- 子系统内部变化会"牵连"调用方
- 学习成本高、可用性差
外观模式(Facade Pattern) 就是为了解决这一类问题:
用一个"外观类"把复杂子系统包装起来,给客户端提供一个统一、简洁的入口。
1. 外观模式是什么?
外观模式:为子系统中的一组接口提供一个一致的高层接口,使得子系统更容易使用。
它的本质是"封装复杂度",不需要知道内部怎么做,只需要调用外观提供的方法即可。
2. 外观模式能解决什么问题?
外观模式通常用于以下场景:
-
隐藏复杂性
- 把多个步骤、多个模块的调用流程封装成一个方法
-
减少耦合
- 客户端不直接依赖复杂子系统的类,只依赖 Facade
-
提升可读性/可维护性
- 业务方只关注"我要什么结果",而不是"怎么一步步实现"
-
便于演进
- 子系统内部替换/升级时,只需要调整 Facade(客户端通常不受影响)
3. 实现思路
实现时通常遵循:
- 先有多个"子系统"类:
子系统A、子系统B、子系统C... - 再定义一个"外观类":
Facade Facade里封装一套统一的流程,把对多个子系统类的调用整合起来- 客户端只与
Facade交互
4. 示例
这里用一个经典场景:家庭影院系统(播放电影需要音响、投影、播放器等按顺序协作)。
4.1 子系统(多个复杂模块)
java
class Projector {
public void on() {
System.out.println("投影仪开机");
}
public void setInput(String input) {
System.out.println("投影仪设置输入源:" + input);
}
}
class SoundSystem {
public void on() {
System.out.println("音响开机");
}
public void setVolume(int volume) {
System.out.println("音响音量:" + volume);
}
}
class Player {
public void on() {
System.out.println("播放器启动");
}
public void play(String movie) {
System.out.println("开始播放:" + movie);
}
}
4.2 外观类(Facade:统一入口)
java
public class HomeTheaterFacade {
private final Projector projector;
private final SoundSystem soundSystem;
private final Player player;
public HomeTheaterFacade(Projector projector, SoundSystem soundSystem, Player player) {
this.projector = projector;
this.soundSystem = soundSystem;
this.player = player;
}
// 外观提供给客户端的简单方法:一键播放
public void playMovie(String movie) {
// 内部复杂流程都封装在这里
projector.on();
projector.setInput("HDMI1");
soundSystem.on();
soundSystem.setVolume(8);
player.on();
player.play(movie);
System.out.println("家庭影院:播放流程已完成");
}
}
4.3 客户端调用(不需要关心内部细节)
java
public class Client {
public static void main(String[] args) {
HomeTheaterFacade facade = new HomeTheaterFacade(
new Projector(),
new SoundSystem(),
new Player()
);
facade.playMovie("Inception");
}
}
客户端只做一件事:调用
playMovie(),无需关心投影仪/音响/播放器的细节顺序。
5. 外观模式的典型结构
- Facade(外观):统一入口,对外提供简化方法
- Subsystem(子系统):具体实现细节,由 Facade 编排调用
- Client(客户端):只依赖 Facade
6. 优缺点
6.1 优点
- 降低复杂度:客户端只关心 Facade 的简单接口
- 减少耦合:客户端不直接依赖子系统实现细节
- 更易演进:子系统变化集中在 Facade 里处理
6.2 缺点
- Facade 可能变得臃肿:做太多"流程编排",导致外观类越来越大
- 可能掩盖灵活性:客户端不再直接访问子系统,某些高级用法会受限
7. 外观模式与代理模式/装饰器模式的区别
-
外观模式(Facade) :
重点是"把一堆复杂调用变成一个简单入口 ",属于结构性封装。
-
代理模式(Proxy) :
重点是"代表/控制访问",常见用途是权限、远程、缓存、延迟等。
-
装饰器模式(Decorator) :
重点是"动态叠加功能",强调在不改变原对象基础上逐层增强行为。
一句话记忆:
- Facade:让你"更好用"(简化接口)
- Proxy:替你"管控访问/转发"(代表与控制)
- Decorator:给你"加功能"(扩展职责)
8. 适用场景
适合:
- 子系统复杂,且要提供统一调用方式
- 多个模块需要固定流程编排(例如初始化、启动、发布)
- 希望降低模块之间的耦合
不太适合:
- 所有逻辑都集中到 Facade,导致"上帝类"
- 客户端需要频繁使用子系统的细粒度能力(那外观可能无法覆盖所有需求)
9. 总结
外观模式就是:用一个 Facade 类把复杂子系统封装起来,给客户端提供简单统一的高层接口。
