系列导读:在学习了功能装饰后,我们来看如何简化复杂系统的使用。外观模式为复杂的子系统提供了一个简单统一的接口。本系列同步更新在微信公众号: 码农秋, 欢迎关注。
解决什么问题:为子系统中的一组接口提供一个统一的高层接口,使子系统更容易使用。隐藏系统复杂性,提供简化的调用方式。
在实际项目中,我们经常需要调用多个子系统来完成一个业务操作。比如,用户下单可能需要调用库存系统、支付系统、物流系统、短信系统等。如果让客户端直接调用这些系统,代码会变得复杂且容易出错。
外观模式就像酒店的前台一样,客户只需要告诉前台自己的需求,前台会协调各个部门来满足客户需求。客户不需要知道酒店内部的复杂流程,只需要通过前台这个简单接口就能享受服务。
本文在系列中的位置:
目录
1. 模式概述
外观模式是一种结构型设计模式,它为复杂的子系统提供一个简单的接口。外观模式通过定义一个高层接口,使得子系统更容易使用,同时降低了客户端与子系统之间的耦合度。通过外观类,客户端可以只与高层接口交互,无需了解子系统的复杂实现细节。
1.1. 定义
为子系统中的一组接口提供一个统一的接口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观类封装了子系统的复杂性,对外暴露简洁的操作入口。
1.2. 目的
- 简化复杂系统的使用,提升开发效率。
- 降低系统间的耦合度,便于维护和扩展。
- 提供统一的接口,隐藏子系统实现细节。
2. 使用场景
外观模式适用于以下场景:
-
系统集成
- 需要集成多个子系统,简化调用流程。
- 需要简化系统接口,提升易用性。
- 需要降低系统耦合,便于后续维护。
-
分层设计
- 需要分层架构,隔离不同层次的实现。
- 需要隐藏实现细节,暴露统一接口。
- 需要提供统一入口,便于上层调用。
-
系统重构
- 需要重构复杂系统,简化对外接口。
- 需要保持向后兼容,平滑过渡。
- 需要简化系统使用,降低学习成本。
真实业务背景举例:
- 大型企业的ERP系统,外观类统一封装财务、采购、库存等子系统,简化业务调用。
- Web应用的服务层,外观类统一封装底层DAO、缓存、日志等子系统,提升开发效率。
- 金融行业的支付网关,外观类统一封装多家银行、第三方支付接口,简化业务接入。
- 电商平台的订单处理系统,外观类统一封装库存、物流、支付等子系统,提升业务开发效率。
3. 优缺点分析
3.1. 优点
- 简化使用:简化系统接口,降低使用难度。提高开发效率,便于团队协作。客户端无需了解子系统细节。
- 解耦:降低系统耦合,提升灵活性。便于系统维护和升级。支持子系统独立演化。
- 封装:隐藏实现细节,提升系统安全性。提供统一接口,便于扩展和维护。支持权限控制和安全校验。
3.2. 缺点
- 灵活性:可能限制灵活性,部分高级功能无法暴露。可能影响扩展性,需合理设计外观接口。可能增加系统复杂度,需权衡设计。
- 性能影响:可能影响性能,增加调用开销。可能影响执行效率,需优化实现。需避免外观类成为性能瓶颈。
- 维护成本:需要维护外观类,保证接口一致性。需要处理版本兼容和异常情况。需合理划分职责,避免外观类过于臃肿。
4. 实际应用案例
- 系统集成:多系统集成,统一入口简化调用。服务整合,提升业务开发效率。接口统一,便于外部系统对接。
- 框架设计:框架封装,隐藏底层实现细节。API设计,暴露简洁易用的接口。工具类封装,提升开发效率。
- 应用开发:模块封装,简化业务逻辑。功能整合,提升系统可维护性。接口简化,降低学习和使用成本。
- 系统重构:系统改造,平滑过渡新旧系统。接口升级,兼容历史调用方式。兼容性处理,降低重构风险。
5. 结构与UML类图
@startuml
package "Facade Pattern" #DDDDDD {
class SystemFacade {
+ saveData(path: String, data: String): void
+ loadData(path: String): String
+ deleteData(path: String): void
- fileSystem: FileSystem
- cacheSystem: CacheSystem
- logSystem: LogSystem
}
class FileSystem {
+ save(path: String, data: String): void
+ load(path: String): String
+ deleteFile(path: String): void
}
class CacheSystem {
+ put(key: String, value: String): void
+ get(key: String): String
+ remove(key: String): void
}
class LogSystem {
+ log(message: String): void
+ error(message: String): void
}
SystemFacade o-- FileSystem : fileSystem
SystemFacade o-- CacheSystem : cacheSystem
SystemFacade o-- LogSystem : logSystem
}
@enduml
6. 代码示例
6.1 基本结构示例
业务背景: 系统包含多个子系统,客户端希望通过统一入口简化操作,避免直接依赖各子系统。
package com.example.patterns.facade;
import java.util.Objects;
// 子系统A,提供独立功能
public class SubsystemA {
public void operationA() {
try {
System.out.println("SubsystemA: 执行操作A");
} catch (Exception e) {
System.err.println("SubsystemA操作失败: " + e.getMessage());
throw new RuntimeException("SubsystemA操作失败", e);
}
}
}
// 子系统B,提供独立功能
public class SubsystemB {
public void operationB() {
try {
System.out.println("SubsystemB: 执行操作B");
} catch (Exception e) {
System.err.println("SubsystemB操作失败: " + e.getMessage());
throw new RuntimeException("SubsystemB操作失败", e);
}
}
}
// 子系统C,提供独立功能
public class SubsystemC {
public void operationC() {
try {
System.out.println("SubsystemC: 执行操作C");
} catch (Exception e) {
System.err.println("SubsystemC操作失败: " + e.getMessage());
throw new RuntimeException("SubsystemC操作失败", e);
}
}
}
// 外观类,统一封装子系统操作
public class Facade {
private final SubsystemA subsystemA;
private final SubsystemB subsystemB;
private final SubsystemC subsystemC;
/**
* 构造方法初始化各子系统,便于统一管理
*/
public Facade() {
this.subsystemA = new SubsystemA();
this.subsystemB = new SubsystemB();
this.subsystemC = new SubsystemC();
}
/**
* 统一操作1,封装多个子系统调用
*/
public void operation1() {
System.out.println("Facade: 开始执行复合操作1");
subsystemA.operationA();
subsystemB.operationB();
System.out.println("Facade: 复合操作1完成");
}
/**
* 统一操作2,封装多个子系统调用
*/
public void operation2() {
System.out.println("Facade: 开始执行复合操作2");
subsystemB.operationB();
subsystemC.operationC();
System.out.println("Facade: 复合操作2完成");
}
}
6.2 企业级应用场景:电商订单处理系统
业务背景: 电商订单处理涉及库存、支付、物流等多个子系统,通过外观模式统一封装,简化业务流程。
package com.example.patterns.facade.ecommerce;
import java.math.BigDecimal;
import java.time.LocalDateTime;
// 库存管理子系统
public class InventorySystem {
public boolean checkStock(String productId, int quantity) {
System.out.println("库存系统: 检查商品 " + productId + " 库存数量 " + quantity);
// 模拟库存检查逻辑
return quantity <= 100; // 假设库存充足
}
public void reduceStock(String productId, int quantity) {
System.out.println("库存系统: 扣减商品 " + productId + " 库存 " + quantity + " 件");
}
public void restoreStock(String productId, int quantity) {
System.out.println("库存系统: 恢复商品 " + productId + " 库存 " + quantity + " 件");
}
}
// 支付子系统
public class PaymentSystem {
public boolean processPayment(String orderId, BigDecimal amount, String paymentMethod) {
System.out.println("支付系统: 处理订单 " + orderId + " 支付,金额: ¥" + amount + ",支付方式: " + paymentMethod);
// 模拟支付处理逻辑
return amount.compareTo(BigDecimal.valueOf(10000)) <= 0; // 假设金额小于等于10000时支付成功
}
public void refund(String orderId, BigDecimal amount) {
System.out.println("支付系统: 订单 " + orderId + " 退款,金额: ¥" + amount);
}
}
// 物流子系统
public class LogisticsSystem {
public String createShipment(String orderId, String address) {
String trackingNumber = "TN" + System.currentTimeMillis();
System.out.println("物流系统: 创建订单 " + orderId + " 物流单,跟踪号: " + trackingNumber + ",配送地址: " + address);
return trackingNumber;
}
public void cancelShipment(String trackingNumber) {
System.out.println("物流系统: 取消物流单 " + trackingNumber);
}
}
// 订单管理子系统
public class OrderSystem {
public String createOrder(String customerId, String productId, int quantity, BigDecimal price) {
String orderId = "ORD" + System.currentTimeMillis();
System.out.println("订单系统: 创建订单 " + orderId + ",客户: " + customerId + ",商品: " + productId);
return orderId;
}
public void updateOrderStatus(String orderId, String status) {
System.out.println("订单系统: 更新订单 " + orderId + " 状态为: " + status);
}
public void cancelOrder(String orderId) {
System.out.println("订单系统: 取消订单 " + orderId);
}
}
// 通知子系统
public class NotificationSystem {
public void sendOrderConfirmation(String customerId, String orderId) {
System.out.println("通知系统: 向客户 " + customerId + " 发送订单 " + orderId + " 确认通知");
}
public void sendShippingNotification(String customerId, String orderId, String trackingNumber) {
System.out.println("通知系统: 向客户 " + customerId + " 发送订单 " + orderId + " 发货通知,跟踪号: " + trackingNumber);
}
public void sendCancellationNotification(String customerId, String orderId) {
System.out.println("通知系统: 向客户 " + customerId + " 发送订单 " + orderId + " 取消通知");
}
}
// 订单处理外观类
public class OrderProcessingFacade {
private final InventorySystem inventorySystem;
private final PaymentSystem paymentSystem;
private final LogisticsSystem logisticsSystem;
private final OrderSystem orderSystem;
private final NotificationSystem notificationSystem;
public OrderProcessingFacade() {
this.inventorySystem = new InventorySystem();
this.paymentSystem = new PaymentSystem();
this.logisticsSystem = new logisticsSystem();
this.orderSystem = new OrderSystem();
this.notificationSystem = new NotificationSystem();
}
/**
* 处理订单 - 统一的业务入口
*/
public String processOrder(String customerId, String productId, int quantity, BigDecimal price, String paymentMethod, String address) {
System.out.println("订单处理外观: 开始处理订单");
try {
// 1. 检查库存
if (!inventorySystem.checkStock(productId, quantity)) {
throw new RuntimeException("库存不足");
}
// 2. 创建订单
String orderId = orderSystem.createOrder(customerId, productId, quantity, price);
// 3. 扣减库存
inventorySystem.reduceStock(productId, quantity);
// 4. 处理支付
BigDecimal totalAmount = price.multiply(BigDecimal.valueOf(quantity));
if (!paymentSystem.processPayment(orderId, totalAmount, paymentMethod)) {
// 支付失败,恢复库存
inventorySystem.restoreStock(productId, quantity);
orderSystem.cancelOrder(orderId);
throw new RuntimeException("支付失败");
}
// 5. 创建物流
String trackingNumber = logisticsSystem.createShipment(orderId, address);
// 6. 更新订单状态
orderSystem.updateOrderStatus(orderId, "已确认");
// 7. 发送通知
notificationSystem.sendOrderConfirmation(customerId, orderId);
notificationSystem.sendShippingNotification(customerId, orderId, trackingNumber);
System.out.println("订单处理外观: 订单 " + orderId + " 处理完成");
return orderId;
} catch (Exception e) {
System.err.println("订单处理外观: 订单处理失败 - " + e.getMessage());
throw new RuntimeException("订单处理失败", e);
}
}
/**
* 取消订单 - 统一的业务入口
*/
public void cancelOrder(String customerId, String orderId, String productId, int quantity) {
System.out.println("订单处理外观: 开始取消订单 " + orderId);
try {
// 1. 取消物流
// logisticsSystem.cancelShipment(trackingNumber); // 需要根据订单查询跟踪号
// 2. 处理退款
// paymentSystem.refund(orderId, totalAmount); // 需要根据订单查询金额
// 3. 恢复库存
inventorySystem.restoreStock(productId, quantity);
// 4. 更新订单状态
orderSystem.cancelOrder(orderId);
// 5. 发送通知
notificationSystem.sendCancellationNotification(customerId, orderId);
System.out.println("订单处理外观: 订单 " + orderId + " 取消完成");
} catch (Exception e) {
System.err.println("订单处理外观: 订单取消失败 - " + e.getMessage());
throw new RuntimeException("订单取消失败", e);
}
}
}
7. 测试用例
业务背景: 验证外观模式的核心功能,包括基本外观功能和订单处理系统。
package com.example.patterns.facade.test;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;
import static org.junit.jupiter.api.Assertions.*;
import java.math.BigDecimal;
public class FacadePatternTest {
private Facade basicFacade;
private OrderProcessingFacade orderFacade;
@BeforeEach
public void setUp() {
basicFacade = new Facade();
orderFacade = new OrderProcessingFacade();
}
@Test
public void testBasicFacadeOperations() {
// 测试基本外观操作
assertDoesNotThrow(() -> {
basicFacade.operation1();
basicFacade.operation2();
});
}
@Test
public void testOrderProcessingSuccess() {
// 测试成功的订单处理
String orderId = orderFacade.processOrder(
"CUST001",
"PROD001",
2,
BigDecimal.valueOf(99.99),
"支付宝",
"北京市朝阳区xxx"
);
assertNotNull("订单ID不应为空", orderId);
assertTrue("订单ID应以ORD开头", orderId.startsWith("ORD"));
}
@Test
public void testOrderProcessingFailure() {
// 测试订单处理失败(金额过大导致支付失败)
assertThrows(RuntimeException.class, () -> {
orderFacade.processOrder(
"CUST002",
"PROD002",
1,
BigDecimal.valueOf(20000), // 超过支付限额
"微信支付",
"上海市浦东新区xxx"
);
});
}
@Test
public void testOrderCancellation() {
// 测试订单取消
assertDoesNotThrow(() -> {
orderFacade.cancelOrder("CUST001", "ORD123456", "PROD001", 2);
});
}
}
8. 常见误区与反例
8.1 常见误区
- 误区1 :外观类承担过多职责,成为"上帝类"
- 误区2 :外观类与子系统强耦合,难以扩展
- 误区3 :忽略异常处理,没有统一的错误处理机制
8.2 反例分析
- 反例1 :直接在外观类中实现业务逻辑,违反单一职责原则
- 反例2 :外观类暴露子系统的实现细节
- 反例3 :没有考虑并发安全问题
9. 最佳实践
9.1 设计原则
- 保持外观类的简洁性 :只做协调和封装,不包含复杂业务逻辑
- 合理划分子系统 :确保子系统职责单一且松散耦合
- 提供多层次的外观 :根据不同的使用场景提供不同级别的外观
9.2 实现技巧
- 使用依赖注入 :提高外观类的可测试性和可配置性
- 实现统一的异常处理 :在外观层统一处理和转换异常
- 支持配置化 :允许通过配置灵活调整子系统的调用策略
10. 参考资料与延伸阅读
- 《设计模式:可复用面向对象软件的基础》GoF
- Effective Java(中文版)
- https://refactoringguru.cn/design-patterns/facade
- https://www.baeldung.com/java-facade-pattern
本文为设计模式系列第13篇,后续每篇将聚焦一个设计模式或设计原则,深入讲解实现与应用,敬请关注。微信公众号: 码农秋,欢迎关注。