外观模式 (Facade)
📖 通俗理解
想象你去饭店吃饭:
- 自己做饭:买菜 → 洗菜 → 切菜 → 炒菜 → 装盘 → 洗碗(好累!)
- 去饭店:只需要说"来份宫保鸡丁",服务员搞定一切
饭店就是一个外观,它把复杂的做菜过程封装起来,对外只提供一个简单的接口:"点菜"。
外观模式就是:为复杂的子系统提供一个统一的、简单的接口。
🎯 解决什么问题?
问题:系统中有很多子模块,客户端需要分别调用它们,很麻烦。
// 没有外观模式,客户端需要这样做:
CPU cpu = new CPU();
Memory memory = new Memory();
HardDrive hd = new HardDrive();
cpu.freeze();
memory.load(0, hd.read(0, 1024));
cpu.jump(0);
cpu.execute();
解决:提供一个外观类,把复杂的调用封装起来。
// 有了外观模式,只需要:
Computer computer = new Computer();
computer.start();
🌰 生活中的例子
- 一键启动汽车:按一下按钮,自动完成解锁、通电、启动等
- 点外卖:下单即可,不用关心商家如何做、骑手如何送
- 遥控器:一个按钮开电视,不用手动调信号、亮度等
- 智能家居:"回家模式"一键开灯、开空调、关窗帘
💻 Java 代码实现
场景:电脑启动
电脑启动需要 CPU、内存、硬盘等多个组件协同工作,我们用外观模式封装这个过程。
第一步:定义子系统
/**
* 子系统:CPU
*/
public class CPU {
public void freeze() {
System.out.println("CPU: 冻结处理器...");
}
public void jump(long position) {
System.out.println("CPU: 跳转到位置 " + position);
}
public void execute() {
System.out.println("CPU: 执行指令...");
}
}
/**
* 子系统:内存
*/
public class Memory {
public void load(long position, byte[] data) {
System.out.println("内存: 加载数据到位置 " + position +
",大小: " + data.length + " bytes");
}
}
/**
* 子系统:硬盘
*/
public class HardDrive {
public byte[] read(long lba, int size) {
System.out.println("硬盘: 从扇区 " + lba + " 读取 " + size + " 字节");
return new byte[size];
}
}
/**
* 子系统:电源
*/
public class Power {
public void on() {
System.out.println("电源: 接通电源...");
}
public void off() {
System.out.println("电源: 断开电源...");
}
}
/**
* 子系统:BIOS
*/
public class BIOS {
public void check() {
System.out.println("BIOS: 自检中...");
}
public void loadBootSector() {
System.out.println("BIOS: 加载引导扇区...");
}
}
第二步:创建外观类
/**
* 外观类:电脑
* 封装了所有子系统的复杂交互
*/
public class Computer {
private CPU cpu;
private Memory memory;
private HardDrive hardDrive;
private Power power;
private BIOS bios;
public Computer() {
this.cpu = new CPU();
this.memory = new Memory();
this.hardDrive = new HardDrive();
this.power = new Power();
this.bios = new BIOS();
}
/**
* 开机 - 对外提供的简单接口
*/
public void start() {
System.out.println("====== 电脑开机 ======");
// 1. 接通电源
power.on();
// 2. BIOS 自检
bios.check();
bios.loadBootSector();
// 3. CPU 初始化
cpu.freeze();
// 4. 从硬盘读取数据到内存
byte[] bootData = hardDrive.read(0, 1024);
memory.load(0, bootData);
// 5. CPU 执行
cpu.jump(0);
cpu.execute();
System.out.println("====== 开机完成 ======");
}
/**
* 关机 - 对外提供的简单接口
*/
public void shutdown() {
System.out.println("====== 电脑关机 ======");
cpu.freeze();
power.off();
System.out.println("====== 关机完成 ======");
}
}
第三步:使用外观
public class Client {
public static void main(String[] args) {
// 用户只需要这样操作:
Computer computer = new Computer();
// 一键开机
computer.start();
System.out.println("\n... 使用电脑中 ...\n");
// 一键关机
computer.shutdown();
}
}
输出:
====== 电脑开机 ======
电源: 接通电源...
BIOS: 自检中...
BIOS: 加载引导扇区...
CPU: 冻结处理器...
硬盘: 从扇区 0 读取 1024 字节
内存: 加载数据到位置 0,大小: 1024 bytes
CPU: 跳转到位置 0
CPU: 执行指令...
====== 开机完成 ======
... 使用电脑中 ...
====== 电脑关机 ======
CPU: 冻结处理器...
电源: 断开电源...
====== 关机完成 ======
🔥 实战案例:订单系统
电商下单涉及多个子系统:库存、支付、物流、通知等。
/**
* 子系统:库存服务
*/
public class InventoryService {
public boolean checkStock(String productId, int quantity) {
System.out.println("库存服务: 检查商品 " + productId + " 库存...");
return true; // 假设有库存
}
public void reduceStock(String productId, int quantity) {
System.out.println("库存服务: 扣减商品 " + productId + " 库存 " + quantity + " 件");
}
}
/**
* 子系统:支付服务
*/
public class PaymentService {
public boolean pay(String orderId, double amount) {
System.out.println("支付服务: 订单 " + orderId + " 支付 ¥" + amount);
return true; // 假设支付成功
}
public void refund(String orderId, double amount) {
System.out.println("支付服务: 订单 " + orderId + " 退款 ¥" + amount);
}
}
/**
* 子系统:物流服务
*/
public class ShippingService {
public String createShipment(String orderId, String address) {
String trackingNumber = "SF" + System.currentTimeMillis();
System.out.println("物流服务: 创建运单 " + trackingNumber +
",收货地址: " + address);
return trackingNumber;
}
}
/**
* 子系统:通知服务
*/
public class NotificationService {
public void sendSMS(String phone, String message) {
System.out.println("通知服务: 发送短信到 " + phone + ": " + message);
}
public void sendEmail(String email, String subject, String content) {
System.out.println("通知服务: 发送邮件到 " + email + ": " + subject);
}
}
/**
* 外观类:订单服务
*/
public class OrderFacade {
private InventoryService inventoryService;
private PaymentService paymentService;
private ShippingService shippingService;
private NotificationService notificationService;
public OrderFacade() {
this.inventoryService = new InventoryService();
this.paymentService = new PaymentService();
this.shippingService = new ShippingService();
this.notificationService = new NotificationService();
}
/**
* 下单 - 统一接口,封装复杂流程
*/
public boolean placeOrder(String orderId, String productId,
int quantity, double amount,
String address, String phone) {
System.out.println("========== 开始处理订单 " + orderId + " ==========");
// 1. 检查库存
if (!inventoryService.checkStock(productId, quantity)) {
System.out.println("库存不足,下单失败!");
return false;
}
// 2. 扣减库存
inventoryService.reduceStock(productId, quantity);
// 3. 支付
if (!paymentService.pay(orderId, amount)) {
System.out.println("支付失败,回滚库存...");
// 这里应该回滚库存
return false;
}
// 4. 创建物流
String trackingNumber = shippingService.createShipment(orderId, address);
// 5. 发送通知
notificationService.sendSMS(phone,
"您的订单 " + orderId + " 已发货,运单号: " + trackingNumber);
System.out.println("========== 订单处理完成 ==========");
return true;
}
}
使用方式:
public class Client {
public static void main(String[] args) {
OrderFacade orderFacade = new OrderFacade();
// 客户端只需要调用一个方法
boolean success = orderFacade.placeOrder(
"ORD20240115001", // 订单号
"SKU12345", // 商品ID
2, // 数量
299.00, // 金额
"北京市朝阳区xxx", // 地址
"13800138000" // 手机号
);
System.out.println("\n下单" + (success ? "成功" : "失败"));
}
}
输出:
========== 开始处理订单 ORD20240115001 ==========
库存服务: 检查商品 SKU12345 库存...
库存服务: 扣减商品 SKU12345 库存 2 件
支付服务: 订单 ORD20240115001 支付 ¥299.0
物流服务: 创建运单 SF1705289123456,收货地址: 北京市朝阳区xxx
通知服务: 发送短信到 13800138000: 您的订单 ORD20240115001 已发货,运单号: SF1705289123456
========== 订单处理完成 ==========
下单成功
📊 类图结构
┌─────────────────────────────────────────────────────────┐
│ Client │
└───────────────────────────┬─────────────────────────────┘
│ 只调用外观
▼
┌────────────────┐
│ Facade │
│ + operation() │
└────────┬───────┘
│ 调用子系统
┌────────────────┼────────────────┐
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ SubSystemA │ │ SubSystemB │ │ SubSystemC │
└────────────┘ └────────────┘ └────────────┘
✅ 适用场景
- 简化复杂系统:为复杂子系统提供简单入口
- 解耦客户端和子系统:客户端不需要知道子系统内部
- 分层系统:在层与层之间建立外观
- 遗留系统包装:为老系统提供新接口
⚠️ 优缺点
优点:
- 简化客户端调用
- 降低耦合度
- 更好的分层
缺点:
- 不符合开闭原则(增加功能需要修改外观类)
- 可能成为"上帝类"
小结
外观模式的核心:为复杂的子系统提供一个简单统一的接口。
简单记忆:外观模式 = 服务台 = 一站式服务
👉 下一篇:享元模式
