文章目录
-
- 什么是外观模式?
- 核心思想
- 生活中的外观模式
- 模式结构
- 基础示例:家庭影院系统
-
- [1. 子系统类](#1. 子系统类)
- [2. 外观类](#2. 外观类)
- [3. 客户端使用](#3. 客户端使用)
- 完整示例:电商订单系统
-
- [1. 子系统类](#1. 子系统类)
- [2. 订单外观类](#2. 订单外观类)
- [3. 电商客户端](#3. 电商客户端)
- 实际应用示例:银行转账系统
- 外观模式的优点
-
- [1. 简化客户端使用](#1. 简化客户端使用)
- [2. 解耦客户端与子系统](#2. 解耦客户端与子系统)
- [3. 易于维护和扩展](#3. 易于维护和扩展)
- 外观模式的缺点
-
- [1. 不符合开闭原则](#1. 不符合开闭原则)
- [2. 可能成为上帝对象](#2. 可能成为上帝对象)
- 适用场景
- 最佳实践
-
- [1. 保持外观类简单](#1. 保持外观类简单)
- [2. 合理划分外观层次](#2. 合理划分外观层次)
- [3. 考虑使用接口](#3. 考虑使用接口)
- [外观模式 vs 其他模式](#外观模式 vs 其他模式)
- 总结
什么是外观模式?
外观模式(Facade Pattern)是一种结构型设计模式,它提供了一个统一的接口,用来访问子系统中的一群接口。外观模式定义了一个高层接口,让子系统更容易使用。
核心思想
外观模式的核心思想是:提供一个统一的接口,来访问子系统中的多个接口,从而隐藏子系统的复杂性,让客户端更容易使用。
生活中的外观模式
想象一下去餐厅吃饭:
- 你不需要直接跟厨师、服务员、收银员分别打交道
- 只需要跟服务员(外观)点餐
- 服务员会协调后厨、收银等各个部门
- 你得到的是简单统一的点餐体验
服务员就是整个餐厅系统的"外观"!
模式结构
外观模式包含三个核心角色:
- 外观类(Facade):提供统一的接口,知道哪些子系统负责处理请求
- 子系统类(Subsystem Classes):实现子系统的功能,处理外观对象指派的任务
- 客户端(Client):通过外观接口与子系统交互
基础示例:家庭影院系统
1. 子系统类
java
/**
* 投影仪 - 子系统类
*/
public class Projector {
public void on() {
System.out.println("投影仪开启");
}
public void off() {
System.out.println("投影仪关闭");
}
public void setInput(String input) {
System.out.println("投影仪输入源设置为: " + input);
}
public void wideScreenMode() {
System.out.println("投影仪设置为宽屏模式");
}
}
/**
* 音响系统 - 子系统类
*/
public class SoundSystem {
public void on() {
System.out.println("音响系统开启");
}
public void off() {
System.out.println("音响系统关闭");
}
public void setVolume(int level) {
System.out.println("音响音量设置为: " + level);
}
public void setSurroundSound() {
System.out.println("音响设置为环绕声模式");
}
}
/**
* DVD播放器 - 子系统类
*/
public class DvdPlayer {
public void on() {
System.out.println("DVD播放器开启");
}
public void off() {
System.out.println("DVD播放器关闭");
}
public void play(String movie) {
System.out.println("DVD播放器开始播放: " + movie);
}
public void stop() {
System.out.println("DVD播放器停止播放");
}
public void eject() {
System.out.println("DVD播放器弹出光盘");
}
}
/**
* 灯光控制器 - 子系统类
*/
public class LightController {
public void dim(int level) {
System.out.println("灯光调暗到: " + level + "%");
}
public void on() {
System.out.println("灯光全开");
}
public void off() {
System.out.println("灯光关闭");
}
}
/**
* 窗帘控制器 - 子系统类
*/
public class CurtainController {
public void open() {
System.out.println("窗帘打开");
}
public void close() {
System.out.println("窗帘关闭");
}
}
2. 外观类
java
/**
* 家庭影院外观类 - 外观类
* 提供简化的接口来控制复杂的家庭影院系统
*/
public class HomeTheaterFacade {
// 引用所有子系统组件
private Projector projector;
private SoundSystem soundSystem;
private DvdPlayer dvdPlayer;
private LightController lightController;
private CurtainController curtainController;
public HomeTheaterFacade(Projector projector, SoundSystem soundSystem,
DvdPlayer dvdPlayer, LightController lightController,
CurtainController curtainController) {
this.projector = projector;
this.soundSystem = soundSystem;
this.dvdPlayer = dvdPlayer;
this.lightController = lightController;
this.curtainController = curtainController;
}
/**
* 一键开启电影模式
*/
public void watchMovie(String movie) {
System.out.println("🎬 准备观看电影: " + movie);
System.out.println("=" .repeat(40));
curtainController.close(); // 关闭窗帘
lightController.dim(10); // 调暗灯光
projector.on(); // 打开投影仪
projector.setInput("DVD");
projector.wideScreenMode();
soundSystem.on(); // 打开音响
soundSystem.setSurroundSound();
soundSystem.setVolume(20);
dvdPlayer.on(); // 打开DVD
dvdPlayer.play(movie); // 播放电影
System.out.println("=" .repeat(40));
System.out.println("🎯 电影模式准备就绪!");
}
/**
* 一键结束电影
*/
public void endMovie() {
System.out.println("🛑 结束电影播放");
System.out.println("=" .repeat(40));
dvdPlayer.stop(); // 停止DVD
dvdPlayer.eject(); // 弹出光盘
dvdPlayer.off(); // 关闭DVD
soundSystem.off(); // 关闭音响
projector.off(); // 关闭投影仪
lightController.on(); // 打开灯光
curtainController.open(); // 打开窗帘
System.out.println("=" .repeat(40));
System.out.println("✅ 家庭影院系统已关闭");
}
/**
* 一键听音乐模式
*/
public void listenToMusic() {
System.out.println("🎵 切换到音乐模式");
System.out.println("=" .repeat(40));
lightController.dim(30); // 适度调暗灯光
curtainController.open(); // 打开窗帘
soundSystem.on(); // 打开音响
soundSystem.setVolume(15);
System.out.println("=" .repeat(40));
System.out.println("🎧 音乐模式准备就绪!");
}
/**
* 系统状态检查
*/
public void systemStatus() {
System.out.println("📊 系统状态检查:");
System.out.println("- 投影仪: " + (projector != null ? "就绪" : "未连接"));
System.out.println("- 音响系统: " + (soundSystem != null ? "就绪" : "未连接"));
System.out.println("- DVD播放器: " + (dvdPlayer != null ? "就绪" : "未连接"));
System.out.println("- 灯光控制: " + (lightController != null ? "就绪" : "未连接"));
System.out.println("- 窗帘控制: " + (curtainController != null ? "就绪" : "未连接"));
}
}
3. 客户端使用
java
/**
* 家庭影院客户端
*/
public class HomeTheaterClient {
public static void main(String[] args) {
System.out.println("=== 家庭影院系统演示 ===\n");
// 创建子系统组件
Projector projector = new Projector();
SoundSystem soundSystem = new SoundSystem();
DvdPlayer dvdPlayer = new DvdPlayer();
LightController lightController = new LightController();
CurtainController curtainController = new CurtainController();
// 创建外观对象
HomeTheaterFacade homeTheater = new HomeTheaterFacade(
projector, soundSystem, dvdPlayer, lightController, curtainController);
// 检查系统状态
homeTheater.systemStatus();
System.out.println("\n" + "⭐".repeat(50));
// 使用外观的简单接口
homeTheater.watchMovie("阿凡达");
System.out.println("\n" + "⭐".repeat(50));
// 模拟观看一段时间后结束
try {
Thread.sleep(2000); // 模拟观看2秒
} catch (InterruptedException e) {
e.printStackTrace();
}
homeTheater.endMovie();
System.out.println("\n" + "⭐".repeat(50));
// 切换到音乐模式
homeTheater.listenToMusic();
}
}
完整示例:电商订单系统
让我们通过一个更复杂的电商订单系统来深入理解外观模式。
1. 子系统类
java
/**
* 库存服务 - 子系统类
*/
public class InventoryService {
public boolean checkStock(String productId, int quantity) {
System.out.println("📦 检查库存: 商品 " + productId + " 数量 " + quantity);
// 模拟库存检查
boolean inStock = Math.random() > 0.1; // 90%有库存
System.out.println("库存状态: " + (inStock ? "充足" : "缺货"));
return inStock;
}
public void updateStock(String productId, int quantity) {
System.out.println("📦 更新库存: 商品 " + productId + " 减少 " + quantity);
}
public void lockStock(String productId, int quantity) {
System.out.println("🔒 锁定库存: 商品 " + productId + " 数量 " + quantity);
}
}
/**
* 支付服务 - 子系统类
*/
public class PaymentService {
public boolean processPayment(String orderId, double amount, String paymentMethod) {
System.out.println("💳 处理支付: 订单 " + orderId + " 金额 ¥" + amount + " 方式 " + paymentMethod);
// 模拟支付处理
boolean success = Math.random() > 0.05; // 95%成功率
System.out.println("支付结果: " + (success ? "成功" : "失败"));
return success;
}
public void refundPayment(String orderId, double amount) {
System.out.println("🔄 退款处理: 订单 " + orderId + " 金额 ¥" + amount);
}
}
/**
* 物流服务 - 子系统类
*/
public class ShippingService {
public String createShipping(String orderId, String address) {
System.out.println("🚚 创建物流: 订单 " + orderId + " 地址 " + address);
String trackingNumber = "TRK" + System.currentTimeMillis();
System.out.println("物流单号: " + trackingNumber);
return trackingNumber;
}
public void schedulePickup(String trackingNumber) {
System.out.println("📅 安排取件: 物流单号 " + trackingNumber);
}
public void updateShippingStatus(String trackingNumber, String status) {
System.out.println("📝 更新物流状态: " + trackingNumber + " -> " + status);
}
}
/**
* 通知服务 - 子系统类
*/
public class NotificationService {
public void sendOrderConfirmation(String orderId, String email) {
System.out.println("📧 发送订单确认邮件: 订单 " + orderId + " 到 " + email);
}
public void sendShippingNotification(String orderId, String trackingNumber, String email) {
System.out.println("📦 发送发货通知: 订单 " + orderId + " 物流单号 " + trackingNumber + " 到 " + email);
}
public void sendPaymentFailure(String orderId, String email) {
System.out.println("❌ 发送支付失败通知: 订单 " + orderId + " 到 " + email);
}
}
/**
* 优惠券服务 - 子系统类
*/
public class CouponService {
public boolean validateCoupon(String couponCode, double orderAmount) {
System.out.println("🎫 验证优惠券: " + couponCode + " 订单金额 ¥" + orderAmount);
boolean valid = couponCode != null && couponCode.startsWith("DISCOUNT");
System.out.println("优惠券状态: " + (valid ? "有效" : "无效"));
return valid;
}
public double applyCoupon(String couponCode, double orderAmount) {
System.out.println("💰 应用优惠券: " + couponCode);
double discount = orderAmount * 0.1; // 10%折扣
System.out.println("优惠金额: ¥" + discount);
return orderAmount - discount;
}
}
2. 订单外观类
java
import java.util.List;
/**
* 订单处理外观类
* 简化订单处理流程,隐藏底层复杂性
*/
public class OrderProcessingFacade {
private InventoryService inventoryService;
private PaymentService paymentService;
private ShippingService shippingService;
private NotificationService notificationService;
private CouponService couponService;
public OrderProcessingFacade() {
this.inventoryService = new InventoryService();
this.paymentService = new PaymentService();
this.shippingService = new ShippingService();
this.notificationService = new NotificationService();
this.couponService = new CouponService();
}
/**
* 一键下单
*/
public OrderResult placeOrder(OrderRequest request) {
System.out.println("🛒 开始处理订单: " + request.getOrderId());
System.out.println("=" .repeat(50));
OrderResult result = new OrderResult(request.getOrderId());
try {
// 1. 检查库存
if (!inventoryService.checkStock(request.getProductId(), request.getQuantity())) {
result.setSuccess(false);
result.setMessage("商品库存不足");
return result;
}
// 2. 验证优惠券
double finalAmount = request.getAmount();
if (request.getCouponCode() != null) {
if (couponService.validateCoupon(request.getCouponCode(), request.getAmount())) {
finalAmount = couponService.applyCoupon(request.getCouponCode(), request.getAmount());
}
}
// 3. 处理支付
if (!paymentService.processPayment(request.getOrderId(), finalAmount, request.getPaymentMethod())) {
result.setSuccess(false);
result.setMessage("支付处理失败");
notificationService.sendPaymentFailure(request.getOrderId(), request.getCustomerEmail());
return result;
}
// 4. 锁定库存
inventoryService.lockStock(request.getProductId(), request.getQuantity());
// 5. 创建物流
String trackingNumber = shippingService.createShipping(request.getOrderId(), request.getShippingAddress());
shippingService.schedulePickup(trackingNumber);
// 6. 发送通知
notificationService.sendOrderConfirmation(request.getOrderId(), request.getCustomerEmail());
notificationService.sendShippingNotification(request.getOrderId(), trackingNumber, request.getCustomerEmail());
// 7. 更新库存
inventoryService.updateStock(request.getProductId(), request.getQuantity());
result.setSuccess(true);
result.setMessage("订单处理成功");
result.setTrackingNumber(trackingNumber);
result.setFinalAmount(finalAmount);
System.out.println("=" .repeat(50));
System.out.println("✅ 订单处理完成: " + request.getOrderId());
} catch (Exception e) {
result.setSuccess(false);
result.setMessage("订单处理异常: " + e.getMessage());
System.out.println("❌ 订单处理失败: " + e.getMessage());
}
return result;
}
/**
* 取消订单
*/
public boolean cancelOrder(String orderId, double amount) {
System.out.println("🗑️ 取消订单: " + orderId);
System.out.println("=" .repeat(50));
try {
paymentService.refundPayment(orderId, amount);
System.out.println("✅ 订单取消完成: " + orderId);
return true;
} catch (Exception e) {
System.out.println("❌ 订单取消失败: " + e.getMessage());
return false;
}
}
/**
* 查询订单状态
*/
public void checkOrderStatus(String orderId, String trackingNumber) {
System.out.println("🔍 查询订单状态: " + orderId);
shippingService.updateShippingStatus(trackingNumber, "运输中");
System.out.println("订单状态: 运输中");
}
}
/**
* 订单请求类
*/
class OrderRequest {
private String orderId;
private String productId;
private int quantity;
private double amount;
private String paymentMethod;
private String customerEmail;
private String shippingAddress;
private String couponCode;
// 构造函数
public OrderRequest(String orderId, String productId, int quantity, double amount,
String paymentMethod, String customerEmail, String shippingAddress) {
this.orderId = orderId;
this.productId = productId;
this.quantity = quantity;
this.amount = amount;
this.paymentMethod = paymentMethod;
this.customerEmail = customerEmail;
this.shippingAddress = shippingAddress;
}
// Getter和Setter方法
public String getOrderId() { return orderId; }
public String getProductId() { return productId; }
public int getQuantity() { return quantity; }
public double getAmount() { return amount; }
public String getPaymentMethod() { return paymentMethod; }
public String getCustomerEmail() { return customerEmail; }
public String getShippingAddress() { return shippingAddress; }
public String getCouponCode() { return couponCode; }
public void setCouponCode(String couponCode) { this.couponCode = couponCode; }
}
/**
* 订单结果类
*/
class OrderResult {
private String orderId;
private boolean success;
private String message;
private String trackingNumber;
private double finalAmount;
public OrderResult(String orderId) {
this.orderId = orderId;
}
// Getter和Setter方法
public String getOrderId() { return orderId; }
public boolean isSuccess() { return success; }
public void setSuccess(boolean success) { this.success = success; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public String getTrackingNumber() { return trackingNumber; }
public void setTrackingNumber(String trackingNumber) { this.trackingNumber = trackingNumber; }
public double getFinalAmount() { return finalAmount; }
public void setFinalAmount(double finalAmount) { this.finalAmount = finalAmount; }
@Override
public String toString() {
return String.format("订单%s: %s %s", orderId,
success ? "成功" : "失败", message);
}
}
3. 电商客户端
java
/**
* 电商系统客户端
*/
public class ECommerceClient {
public static void main(String[] args) {
System.out.println("=== 电商订单系统演示 ===\n");
// 创建订单处理外观
OrderProcessingFacade orderProcessor = new OrderProcessingFacade();
// 示例1:正常下单流程
demonstrateNormalOrder(orderProcessor);
// 示例2:使用优惠券下单
demonstrateCouponOrder(orderProcessor);
// 示例3:支付失败情况
demonstratePaymentFailure(orderProcessor);
// 示例4:取消订单
demonstrateOrderCancellation(orderProcessor);
}
/**
* 演示正常下单流程
*/
private static void demonstrateNormalOrder(OrderProcessingFacade processor) {
System.out.println("1. 正常下单流程演示:");
System.out.println("-".repeat(50));
OrderRequest request = new OrderRequest(
"ORDER_001", "PRODUCT_123", 2, 199.99,
"支付宝", "customer@example.com", "北京市朝阳区xxx"
);
OrderResult result = processor.placeOrder(request);
System.out.println("下单结果: " + result);
if (result.isSuccess()) {
System.out.println("物流单号: " + result.getTrackingNumber());
System.out.println("实付金额: ¥" + result.getFinalAmount());
}
}
/**
* 演示使用优惠券下单
*/
private static void demonstrateCouponOrder(OrderProcessingFacade processor) {
System.out.println("\n2. 使用优惠券下单演示:");
System.out.println("-".repeat(50));
OrderRequest request = new OrderRequest(
"ORDER_002", "PRODUCT_456", 1, 299.00,
"微信支付", "vip@example.com", "上海市浦东新区xxx"
);
request.setCouponCode("DISCOUNT10");
OrderResult result = processor.placeOrder(request);
System.out.println("下单结果: " + result);
if (result.isSuccess()) {
System.out.println("原价: ¥299.00");
System.out.println("优惠后: ¥" + result.getFinalAmount());
System.out.println("节省: ¥" + (299.00 - result.getFinalAmount()));
}
}
/**
* 演示支付失败情况
*/
private static void demonstratePaymentFailure(OrderProcessingFacade processor) {
System.out.println("\n3. 支付失败情况演示:");
System.out.println("-".repeat(50));
// 注意:由于支付有5%失败率,可能需要多次运行才能看到失败情况
OrderRequest request = new OrderRequest(
"ORDER_003", "PRODUCT_789", 1, 99.50,
"信用卡", "test@example.com", "广州市天河区xxx"
);
OrderResult result = processor.placeOrder(request);
System.out.println("下单结果: " + result);
}
/**
* 演示取消订单
*/
private static void demonstrateOrderCancellation(OrderProcessingFacade processor) {
System.out.println("\n4. 取消订单演示:");
System.out.println("-".repeat(50));
boolean success = processor.cancelOrder("ORDER_001", 199.99);
System.out.println("取消结果: " + (success ? "成功" : "失败"));
}
}
实际应用示例:银行转账系统
java
/**
* 银行转账系统外观模式示例
*/
public class BankTransferFacade {
private AccountService accountService;
private TransferService transferService;
private NotificationService notificationService;
private AuditService auditService;
public BankTransferFacade() {
this.accountService = new AccountService();
this.transferService = new TransferService();
this.notificationService = new NotificationService();
this.auditService = new AuditService();
}
/**
* 一键转账
*/
public boolean transfer(String fromAccount, String toAccount, double amount) {
System.out.println("💰 开始转账: " + fromAccount + " -> " + toAccount + " 金额 ¥" + amount);
// 1. 验证账户
if (!accountService.validateAccount(fromAccount) ||
!accountService.validateAccount(toAccount)) {
System.out.println("❌ 账户验证失败");
return false;
}
// 2. 检查余额
if (!accountService.checkBalance(fromAccount, amount)) {
System.out.println("❌ 余额不足");
return false;
}
// 3. 执行转账
if (!transferService.executeTransfer(fromAccount, toAccount, amount)) {
System.out.println("❌ 转账执行失败");
return false;
}
// 4. 记录审计日志
auditService.logTransfer(fromAccount, toAccount, amount);
// 5. 发送通知
notificationService.sendTransferNotification(fromAccount, toAccount, amount);
System.out.println("✅ 转账完成");
return true;
}
}
// 子系统类
class AccountService {
public boolean validateAccount(String account) {
System.out.println("🔍 验证账户: " + account);
return account.startsWith("ACC");
}
public boolean checkBalance(String account, double amount) {
System.out.println("💵 检查余额: " + account + " 金额 ¥" + amount);
return amount <= 10000; // 简单模拟
}
}
class TransferService {
public boolean executeTransfer(String from, String to, double amount) {
System.out.println("🔄 执行转账: " + from + " -> " + to + " ¥" + amount);
return Math.random() > 0.02; // 98%成功率
}
}
class NotificationService {
public void sendTransferNotification(String from, String to, double amount) {
System.out.println("📧 发送转账通知: " + from + " 向 " + to + " 转账 ¥" + amount);
}
}
class AuditService {
public void logTransfer(String from, String to, double amount) {
System.out.println("📝 记录审计日志: 转账 " + from + " -> " + to + " ¥" + amount);
}
}
外观模式的优点
1. 简化客户端使用
java
// 没有外观模式:
inventoryService.checkStock();
paymentService.processPayment();
shippingService.createShipping();
notificationService.sendNotification();
// 有外观模式:
orderFacade.placeOrder(); // 一行代码搞定!
2. 解耦客户端与子系统
java
// 客户端只依赖外观类
OrderProcessingFacade facade = new OrderProcessingFacade();
// 不直接依赖各个服务类,降低耦合度
3. 易于维护和扩展
java
// 修改子系统不影响客户端
public class OrderProcessingFacade {
// 可以轻松添加新的服务,客户端无感知
private NewService newService; // 新增服务
}
外观模式的缺点
1. 不符合开闭原则
java
// 当需要添加新的功能时,可能需要修改外观类
public class OrderProcessingFacade {
// 新增功能可能需要修改这个类
}
2. 可能成为上帝对象
java
// 如果外观类承担太多职责,可能变得过于复杂
public class GodFacade {
// 处理所有事情,违反单一职责原则
}
适用场景
- 要为复杂的子系统提供简单接口时
- 客户端与多个子系统之间存在很多依赖关系时
- 需要构建层次结构的子系统时
- 希望解耦客户端与子系统时
最佳实践
1. 保持外观类简单
java
public class SimpleFacade {
// 每个外观类应该只关注一个特定的功能领域
// 不要让它变成"万能"类
}
2. 合理划分外观层次
java
// 对于复杂系统,可以使用多个外观类
public class OrderFacade { } // 订单相关
public class PaymentFacade { } // 支付相关
public class ShippingFacade { } // 物流相关
3. 考虑使用接口
java
public interface OrderService {
OrderResult placeOrder(OrderRequest request);
}
public class OrderFacade implements OrderService {
// 实现接口,提供更好的灵活性
}
外观模式 vs 其他模式
| 模式 | 目的 | 特点 |
|---|---|---|
| 外观模式 | 简化接口 | 提供统一入口,隐藏复杂性 |
| 适配器模式 | 接口转换 | 使不兼容的接口能够一起工作 |
| 中介者模式 | 协调交互 | 减少对象间的直接依赖 |
总结
外观模式就像是一个"总管家",帮你处理所有复杂的事务,让你只需要关注最终结果。
核心价值:
- 简化复杂系统的使用
- 降低客户端与子系统的耦合度
- 提高代码的可维护性
使用场景:
- 系统有很多复杂的子系统时
- 想要为子系统提供简单统一的接口时
- 需要解耦客户端与子系统时
简单记忆:
复杂系统不用愁,外观模式来帮忙!
一个接口管所有,简单方便又高效。
掌握外观模式,能够让你在面对复杂系统时游刃有余,为用户提供简单优雅的使用体验!