设计模式详解-外观模式
设计模式详解:外观模式
一、模式概述
外观模式(Facade Pattern)是结构型设计模式中最具工程实用价值的模式之一,其核心意图在于为子系统中的一组接口提供一个一致的界面,定义一个高层接口使得子系统更加容易使用。这一模式在复杂系统与客户端之间建立简化的交互层,隐藏内部的 intricate 细节,降低认知负担,同时保持子系统功能的完整性和独立性。
外观模式的命名源自建筑学术语"立面"(Facade),即建筑物面向街道的外部表面。正如建筑立面隐藏了内部的结构框架、管线布局、功能分区,软件外观隐藏了子系统的内部组件、交互协议、调用顺序。客户端只需与整洁的外观交互,无需理解背后的复杂机制。
外观模式的深层价值在于分层抽象与信息隐藏。它不是对子系统的功能削减,而是对子系统交互方式的重新组织。在微服务架构、遗留系统封装、第三方库集成等场景中,外观模式是降低系统耦合度、提升可维护性的关键工具。一个设计良好的外观,能够使原本需要数十行代码、多个对象协调才能完成的操作,简化为单一方法的调用。
二、模式结构
外观模式的结构相对灵活,包含以下核心角色:
外观(Facade):知道哪些子系统类负责处理请求,将客户端请求代理给适当的子系统对象。外观本身通常不实现业务逻辑,仅负责协调和委托。
子系统类(Subsystem Classes):实现子系统的功能,处理由外观对象指派的任务。子系统类不知道外观的存在,它们直接响应请求。
客户端(Client):通过外观与子系统交互,无需直接访问子系统内部组件。客户端也可以绕过外观直接访问子系统(这是外观与适配器的关键区别)。
外观模式的关键特征在于单向依赖:外观依赖子系统,子系统不依赖外观。这种松散耦合使得子系统可以独立演化,外观也可以根据客户端需求灵活调整。
三、深度案例:企业级订单履约系统
以下展示一个真实场景下的外观模式应用——电商平台的订单履约核心,涉及库存、支付、物流、通知、积分、风控等多个子系统的复杂协调。
3.1 子系统层:复杂的内部生态
java
/**
* 子系统A:库存服务
* 处理库存预占、扣减、释放、调拨等
*/
@Service
public class InventoryService {
public InventoryReservation reserve(ReservationRequest request) {
// 检查多仓库存
// 锁定库存(Redis分布式锁)
// 写入预占记录
// 触发补货预警
}
public void deduct(String reservationId) {
// 确认扣减,释放预占
// 更新SKU可售库存
// 同步至搜索引擎
}
public void release(String reservationId) {
// 释放预占库存
// 通知等待队列
}
public List<InventoryAllocation> allocate(String skuId, int quantity,
Address destination) {
// 智能分仓算法
// 考虑仓距、运费、时效、库存深度
}
public void transfer(TransferRequest request) {
// 跨仓调拨
// 生成调拨单
// 同步WMS
}
}
/**
* 子系统B:支付服务
* 处理多种支付渠道的复杂交互
*/
@Service
public class PaymentService {
public PaymentIntent createIntent(PaymentRequest request) {
// 创建支付意图
// 选择最优支付渠道
// 生成收银台参数
}
public PaymentResult confirm(String intentId, ConfirmParams params) {
// 确认支付
// 三方渠道调用
// 对账处理
}
public RefundResult refund(String originalTransactionId, BigDecimal amount,
RefundReason reason) {
// 退款申请
// 渠道退款
// 退积分、优惠券回退
}
public List<Transaction> queryTransactions(String orderId) {
// 查询交易流水
}
}
/**
* 子系统C:物流服务
* 对接多家承运商,处理路由、面单、轨迹
*/
@Service
public class LogisticsService {
public ShippingQuote quote(ShippingRequest request) {
// 多承运商询价
// 时效-成本权衡
}
public Shipment createShipment(CreateShipmentRequest request) {
// 分配运单号
// 生成电子面单
// 推送至承运商
}
public void cancelShipment(String trackingNumber) {
// 拦截取消
// 费用结算
}
public List<TrackingEvent> track(String trackingNumber) {
// 查询轨迹
// 聚合多段运输
}
public void updateAddress(String trackingNumber, Address newAddress) {
// 改址服务
// 承运商交互
}
}
/**
* 子系统D:通知服务
* 多渠道消息触达
*/
@Service
public class NotificationService {
public void sendOrderConfirmation(Order order, Channel channel) {
// 模板渲染
// 渠道发送
}
public void sendShipmentNotice(Shipment shipment, List<Channel> channels) {
// 发货通知
}
public void sendDeliveryReminder(String orderId, LocalDate expectedDate) {
// 派送提醒
}
public void subscribeEvent(String orderId, EventType type,
String callbackUrl) {
// 订阅状态变更事件
}
}
/**
* 子系统E:积分服务
*/
@Service
public class PointsService {
public PointsTransaction earn(String userId, BigDecimal orderAmount,
PointsRule rule) {
// 计算积分
// 写入账户
}
public void deductForRedemption(String userId, int points, String orderId) {
// 积分抵现
}
public void rollback(String transactionId) {
// 回滚积分变动
}
}
/**
* 子系统F:风控服务
*/
@Service
public class RiskControlService {
public RiskAssessment assessOrder(Order order) {
// 设备指纹
// 行为分析
// 关联图谱
}
public void reportEvent(RiskEvent event) {
// 上报风险事件
}
public void addWhitelist(String dimension, String value) {
// 添加白名单
}
}
3.2 外观层:订单履约外观
java
/**
* 外观:订单履约统一入口
* 将6个子系统的数十个接口,收敛为5个核心操作
*/
@Service
public class OrderFulfillmentFacade {
private final InventoryService inventoryService;
private final PaymentService paymentService;
private final LogisticsService logisticsService;
private final NotificationService notificationService;
private final PointsService pointsService;
private final RiskControlService riskControlService;
private final FulfillmentEventPublisher eventPublisher;
private final FulfillmentMetrics metrics;
@Autowired
public OrderFulfillmentFacade(InventoryService inventoryService,
PaymentService paymentService,
LogisticsService logisticsService,
NotificationService notificationService,
PointsService pointsService,
RiskControlService riskControlService,
FulfillmentEventPublisher eventPublisher,
FulfillmentMetrics metrics) {
this.inventoryService = inventoryService;
this.paymentService = paymentService;
this.logisticsService = logisticsService;
this.notificationService = notificationService;
this.pointsService = pointsService;
this.riskControlService = riskControlService;
this.eventPublisher = eventPublisher;
this.metrics = metrics;
}
/**
* 核心操作1:提交订单
* 整合风控、库存、支付意图创建
*/
public OrderSubmissionResult submitOrder(OrderSubmissionRequest request) {
long startTime = System.currentTimeMillis();
try {
// 1. 风控检查(同步阻塞,不通过直接拒绝)
RiskAssessment risk = riskControlService.assessOrder(
convertToRiskOrder(request));
if (risk.getDecision() == RiskDecision.REJECT) {
return OrderSubmissionResult.rejected(risk.getReasonCode());
}
if (risk.getDecision() == RiskDecision.CHALLENGE) {
// 触发二次验证
return OrderSubmissionResult.challenge(risk.getChallengeMethod());
}
// 2. 库存预占(带分布式锁,防止超卖)
List<ReservationRequest> reservations = request.getItems().stream()
.map(item -> ReservationRequest.builder()
.skuId(item.getSkuId())
.quantity(item.getQuantity())
.warehouseHint(item.getWarehouseHint())
.build())
.collect(Collectors.toList());
List<InventoryReservation> reserved = new ArrayList<>();
try {
for (ReservationRequest resReq : reservations) {
InventoryReservation res = inventoryService.reserve(resReq);
reserved.add(res);
}
} catch (InsufficientInventoryException e) {
// 部分失败回滚
reserved.forEach(r -> inventoryService.release(r.getReservationId()));
return OrderSubmissionResult.failed(FailureReason.INSUFFICIENT_INVENTORY);
}
// 3. 创建支付意图
PaymentIntent paymentIntent = paymentService.createIntent(
PaymentRequest.builder()
.orderId(request.getOrderId())
.amount(calculateTotalAmount(request))
.currency(request.getCurrency())
.paymentMethods(request.getPreferredPaymentMethods())
.installmentPreference(request.getInstallmentPreference())
.build());
// 4. 积分预计算(展示用,实际发放在支付后)
PointsPreview pointsPreview = pointsService.earn(
request.getUserId(),
calculatePayableAmount(request),
PointsRule.ORDER_EARN
);
// 5. 组装结果
OrderSubmissionResult result = OrderSubmissionResult.builder()
.orderId(request.getOrderId())
.status(OrderStatus.PENDING_PAYMENT)
.reservationIds(reserved.stream()
.map(InventoryReservation::getReservationId)
.collect(Collectors.toList()))
.paymentIntent(PaymentIntentDTO.builder()
.intentId(paymentIntent.getId())
.clientSecret(paymentIntent.getClientSecret())
.availableMethods(paymentIntent.getAvailableMethods())
.build())
.pointsPreview(PointsPreviewDTO.builder()
.estimatedPoints(pointsPreview.getEstimatedPoints())
.estimatedValue(pointsPreview.getEstimatedValue())
.build())
.expireAt(paymentIntent.getExpireAt())
.build();
// 6. 发布事件
eventPublisher.publish(new OrderSubmittedEvent(
request.getOrderId(),
request.getUserId(),
reserved.size(),
paymentIntent.getAmount()
));
metrics.recordSubmissionSuccess(System.currentTimeMillis() - startTime);
return result;
} catch (Exception e) {
metrics.recordSubmissionFailure(e.getClass().getSimpleName());
throw new FulfillmentException("订单提交失败", e);
}
}
/**
* 核心操作2:支付确认
* 整合支付确认、库存扣减、积分发放、通知触发
*/
@Transactional
public PaymentConfirmationResult confirmPayment(String orderId,
String paymentIntentId,
ConfirmParams confirmParams) {
long startTime = System.currentTimeMillis();
try {
// 1. 确认支付
PaymentResult paymentResult = paymentService.confirm(paymentIntentId,
confirmParams);
if (!paymentResult.isSuccess()) {
// 支付失败释放库存
releaseInventoryForOrder(orderId);
return PaymentConfirmationResult.failed(paymentResult.getFailureReason());
}
// 2. 确认库存扣减
List<String> reservationIds = getReservationIds(orderId);
reservationIds.forEach(inventoryService::deduct);
// 3. 发放积分
PointsTransaction pointsTx = pointsService.earn(
getUserId(orderId),
paymentResult.getActualAmount(),
PointsRule.ORDER_EARN
);
// 4. 创建物流发货单(异步触发,不阻塞支付确认)
CompletableFuture.runAsync(() -> {
try {
createShipmentAsync(orderId);
} catch (Exception e) {
// 发货失败进入补偿队列,不影响支付结果
enqueueShipmentRetry(orderId);
}
});
// 5. 发送支付成功通知
notificationService.sendOrderConfirmation(
getOrder(orderId),
determineNotificationChannels(getUserId(orderId))
);
// 6. 更新订单状态
updateOrderStatus(orderId, OrderStatus.PAID);
// 7. 发布领域事件
eventPublisher.publish(new OrderPaidEvent(
orderId,
paymentResult.getTransactionId(),
paymentResult.getActualAmount(),
paymentResult.getPaidAt()
));
metrics.recordPaymentSuccess(System.currentTimeMillis() - startTime);
return PaymentConfirmationResult.success(
paymentResult.getTransactionId(),
pointsTx.getPoints()
);
} catch (Exception e) {
metrics.recordPaymentFailure(e.getClass().getSimpleName());
throw new FulfillmentException("支付确认失败", e);
}
}
/**
* 核心操作3:查询订单全景
* 聚合多个子系统的数据,返回统一视图
*/
public OrderPanorama getOrderPanorama(String orderId) {
// 并行查询各子系统数据
CompletableFuture<Order> orderFuture = CompletableFuture
.supplyAsync(() -> getOrder(orderId));
CompletableFuture<List<Transaction>> transactionsFuture = CompletableFuture
.supplyAsync(() -> paymentService.queryTransactions(orderId));
CompletableFuture<List<Shipment>> shipmentsFuture = CompletableFuture
.supplyAsync(() -> getShipments(orderId));
CompletableFuture<List<TrackingEvent>> trackingFuture = shipmentsFuture
.thenCompose(shipments -> {
if (shipments.isEmpty()) return CompletableFuture.completedFuture(
Collections.emptyList());
return CompletableFuture.supplyAsync(() -> shipments.stream()
.flatMap(s -> logisticsService.track(s.getTrackingNumber()).stream())
.collect(Collectors.toList()));
});
CompletableFuture<PointsBalance> pointsFuture = CompletableFuture
.supplyAsync(() -> pointsService.queryOrderRelated(orderId));
// 等待全部完成
CompletableFuture.allOf(orderFuture, transactionsFuture, shipmentsFuture,
trackingFuture, pointsFuture).join();
try {
return OrderPanorama.builder()
.order(orderFuture.get())
.payment(PaymentPanorama.builder()
.transactions(transactionsFuture.get())
.refundableAmount(calculateRefundableAmount(transactionsFuture.get()))
.build())
.logistics(LogisticsPanorama.builder()
.shipments(shipmentsFuture.get())
.latestTracking(trackingFuture.get().stream()
.max(Comparator.comparing(TrackingEvent::getTimestamp))
.orElse(null))
.build())
.points(PointsPanorama.builder()
.earned(pointsFuture.get().getEarned())
.redeemed(pointsFuture.get().getRedeemed())
.build())
.nextActions(determineNextActions(orderFuture.get(),
shipmentsFuture.get()))
.build();
} catch (Exception e) {
throw new FulfillmentException("聚合订单全景失败", e);
}
}
/**
* 核心操作4:取消订单
* 协调支付退款、库存释放、物流拦截、积分回滚
*/
@Transactional
public CancellationResult cancelOrder(String orderId, CancellationReason reason,
String operatorId) {
Order order = getOrder(orderId);
// 校验可取消状态
if (!order.canCancel()) {
return CancellationResult.rejected("订单状态不可取消: " + order.getStatus());
}
// 1. 退款(根据已支付金额)
if (order.getPaidAmount().compareTo(BigDecimal.ZERO) > 0) {
RefundResult refund = paymentService.refund(
order.getLastTransactionId(),
order.getPaidAmount(),
RefundReason.of(reason)
);
if (!refund.isSuccess()) {
return CancellationResult.failed("退款失败: " + refund.getErrorMessage());
}
}
// 2. 释放库存
releaseInventoryForOrder(orderId);
// 3. 拦截物流(如已发货)
List<Shipment> shipments = getShipments(orderId);
for (Shipment shipment : shipments) {
if (shipment.getStatus() == ShipmentStatus.SHIPPED) {
logisticsService.cancelShipment(shipment.getTrackingNumber());
}
}
// 4. 回滚积分
pointsService.rollback(orderId);
// 5. 更新状态
updateOrderStatus(orderId, OrderStatus.CANCELLED);
// 6. 通知
notificationService.sendOrderCancellationNotice(order, reason);
// 7. 风控标记
riskControlService.reportEvent(RiskEvent.builder()
.type(RiskEventType.ORDER_CANCELLED)
.orderId(orderId)
.reason(reason)
.operatorId(operatorId)
.build());
return CancellationResult.success();
}
/**
* 核心操作5:修改收货地址
* 判断修改时机,协调物流子系统
*/
public AddressModificationResult modifyAddress(String orderId, Address newAddress) {
Order order = getOrder(orderId);
// 未发货:直接修改
if (order.getStatus() == OrderStatus.PAID) {
updateOrderAddress(orderId, newAddress);
// 重新分配仓库可能影响库存预占
reallocateInventoryIfNeeded(orderId, newAddress);
return AddressModificationResult.success();
}
// 已发货:尝试物流改址
if (order.getStatus() == OrderStatus.SHIPPED) {
List<Shipment> shipments = getShipments(orderId);
for (Shipment shipment : shipments) {
if (!logisticsService.isAddressModifiable(shipment.getTrackingNumber())) {
return AddressModificationResult.failed(
"包裹" + shipment.getTrackingNumber() + "已进入末端配送,不可改址");
}
}
// 全部可改址
for (Shipment shipment : shipments) {
logisticsService.updateAddress(shipment.getTrackingNumber(), newAddress);
}
updateOrderAddress(orderId, newAddress);
return AddressModificationResult.successWithFee(calculateAddressChangeFee());
}
return AddressModificationResult.rejected("当前订单状态不支持改址");
}
// ========== 私有辅助方法 ==========
private void createShipmentAsync(String orderId) {
Order order = getOrder(orderId);
List<InventoryAllocation> allocations = inventoryService
.getAllocations(orderId);
for (InventoryAllocation allocation : allocations) {
ShippingQuote quote = logisticsService.quote(ShippingRequest.builder()
.origin(allocation.getWarehouseAddress())
.destination(order.getShippingAddress())
.weight(allocation.getTotalWeight())
.dimensions(allocation.getPackageDimensions())
.serviceLevel(order.getShippingServiceLevel())
.build());
Shipment shipment = logisticsService.createShipment(
CreateShipmentRequest.builder()
.orderId(orderId)
.allocationId(allocation.getAllocationId())
.carrier(quote.getSelectedCarrier())
.serviceType(quote.getServiceType())
.packageInfo(allocation.toPackageInfo())
.recipient(order.getRecipientInfo())
.build());
notificationService.subscribeEvent(orderId, EventType.DELIVERY_UPDATE,
buildCallbackUrl(orderId));
}
}
private void releaseInventoryForOrder(String orderId) {
List<String> reservationIds = getReservationIds(orderId);
reservationIds.forEach(inventoryService::release);
}
private List<Channel> determineNotificationChannels(String userId) {
UserPreference preference = getUserPreference(userId);
List<Channel> channels = new ArrayList<>();
if (preference.isEmailNotificationEnabled()) channels.add(Channel.EMAIL);
if (preference.isSmsNotificationEnabled()) channels.add(Channel.SMS);
if (preference.isPushNotificationEnabled()) channels.add(Channel.PUSH);
return channels;
}
private List<Action> determineNextActions(Order order, List<Shipment> shipments) {
List<Action> actions = new ArrayList<>();
if (order.canCancel()) actions.add(Action.CANCEL);
if (order.canModifyAddress()) actions.add(Action.MODIFY_ADDRESS);
if (!shipments.isEmpty() && shipments.stream().allMatch(
s -> s.getStatus() == ShipmentStatus.DELIVERED)) {
actions.add(Action.APPLY_RETURN);
}
return actions;
}
}
3.3 外观的演进:分层外观与领域外观
随着系统复杂度增长,单一外观可能过于庞大。分层外观将职责按领域拆分:
java
/**
* 分层外观:用户端订单外观
* 面向C端用户,简化操作,隐藏运营细节
*/
@Service
public class CustomerOrderFacade {
private final OrderFulfillmentFacade fulfillmentFacade;
public OrderSubmissionResult placeOrder(OrderSubmissionRequest request) {
// 填充用户上下文
request.setUserId(CurrentUser.getId());
return fulfillmentFacade.submitOrder(request);
}
public OrderPanorama viewOrder(String orderId) {
// 校验订单归属
validateOrderOwnership(orderId, CurrentUser.getId());
return fulfillmentFacade.getOrderPanorama(orderId);
}
public CancellationResult cancelMyOrder(String orderId, CancellationReason reason) {
validateOrderOwnership(orderId, CurrentUser.getId());
return fulfillmentFacade.cancelOrder(orderId, reason, CurrentUser.getId());
}
// 用户端特有:仅暴露有限操作
}
/**
* 分层外观:商家端订单外观
* 面向B端商家,支持批量操作、数据导出
*/
@Service
public class MerchantOrderFacade {
private final OrderFulfillmentFacade fulfillmentFacade;
private final OrderRepository orderRepository;
public Page<MerchantOrderView> listOrders(MerchantOrderQuery query) {
// 商家维度查询
return orderRepository.findByMerchantId(CurrentMerchant.getId(), query);
}
public BatchOperationResult batchShip(BatchShipRequest request) {
// 批量发货
List<CompletableFuture<ShipmentResult>> futures = request.getItems().stream()
.map(item -> CompletableFuture.supplyAsync(() -> {
try {
return fulfillmentFacade.createShipment(item.getOrderId(),
item.getShipParams());
} catch (Exception e) {
return ShipmentResult.failed(item.getOrderId(), e.getMessage());
}
}))
.collect(Collectors.toList());
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
return collectResults(futures);
}
public byte[] exportOrders(OrderExportRequest request) {
// 数据导出,包含商家需要的特殊字段
}
// 商家端特有:库存预警、退款审核等
}
/**
* 分层外观:运营后台外观
* 面向平台运营,支持强制操作、数据修正
*/
@Service
public class AdminOrderFacade {
private final OrderFulfillmentFacade fulfillmentFacade;
@Secured("ROLE_ORDER_ADMIN")
public void forceCancel(String orderId, String reason, String operatorNote) {
// 绕过业务校验,强制取消
fulfillmentFacade.forceCancel(orderId, reason, operatorNote);
}
@Secured("ROLE_ORDER_ADMIN")
public void modifyOrderData(String orderId, OrderDataPatch patch) {
// 数据修正,记录审计日志
}
@Secured("ROLE_ORDER_ADMIN")
public List<OperationLog> queryOperationLogs(String orderId) {
// 查询全链路操作记录
}
}
3.4 外观与防腐层:遗留系统封装
java
/**
* 防腐层外观:封装遗留ERP系统
* 将遗留系统的复杂协议转换为现代接口
*/
@Service
public class LegacyErpFacade {
private final ErpWebServiceClient erpClient; // SOAP客户端
private final ErpDataConverter dataConverter;
private final CacheManager cacheManager;
/**
* 现代接口:获取供应商信息
* 内部转换为遗留系统的多步调用
*/
public SupplierDTO getSupplier(String supplierCode) {
// 尝试缓存
Cache cache = cacheManager.getCache("erp-supplier");
SupplierDTO cached = cache.get(supplierCode, SupplierDTO.class);
if (cached != null) return cached;
try {
// 遗留系统调用1:获取基础信息
GetVendorInfoRequest step1 = new GetVendorInfoRequest();
step1.setVendorCode(supplierCode);
step1.setAuthToken(erpClient.getSessionToken());
GetVendorInfoResponse vendorInfo = erpClient.getVendorInfo(step1);
// 遗留系统调用2:获取银行账户
GetVendorBankRequest step2 = new GetVendorBankRequest();
step2.setVendorInternalId(vendorInfo.getInternalId());
GetVendorBankResponse bankInfo = erpClient.getVendorBank(step2);
// 遗留系统调用3:获取联系人
GetVendorContactsRequest step3 = new GetVendorContactsRequest();
step3.setVendorInternalId(vendorInfo.getInternalId());
GetVendorContactsResponse contacts = erpClient.getVendorContacts(step3);
// 转换并缓存
SupplierDTO result = dataConverter.convert(vendorInfo, bankInfo, contacts);
cache.put(supplierCode, result);
return result;
} catch (ErpFault fault) {
if ("VENDOR_NOT_FOUND".equals(fault.getFaultCode())) {
throw new SupplierNotFoundException(supplierCode);
}
throw new ErpIntegrationException("ERP调用失败", fault);
}
}
/**
* 现代接口:创建采购订单
* 内部处理遗留系统的状态机和工作流
*/
public PurchaseOrderCreationResult createPurchaseOrder(PurchaseOrderRequest request) {
// 转换请求格式
ErpPORCreateRequest erpRequest = dataConverter.toErpFormat(request);
// 提交创建
ErpPORCreateResponse response = erpClient.createPO(erpRequest);
if (!"SUCCESS".equals(response.getStatus())) {
return PurchaseOrderCreationResult.failed(response.getErrorMessages());
}
// 遗留系统异步审批,轮询状态
String poNumber = response.getPoNumber();
ApprovalStatus approvalStatus = pollApprovalStatus(poNumber,
Duration.ofMinutes(10));
if (approvalStatus == ApprovalStatus.APPROVED) {
return PurchaseOrderCreationResult.success(poNumber);
} else if (approvalStatus == ApprovalStatus.REJECTED) {
return PurchaseOrderCreationResult.rejected("审批未通过");
} else {
return PurchaseOrderCreationResult.pending(poNumber);
}
}
private ApprovalStatus pollApprovalStatus(String poNumber, Duration timeout) {
long deadline = System.currentTimeMillis() + timeout.toMillis();
while (System.currentTimeMillis() < deadline) {
ErpPOStatusResponse status = erpClient.getPOStatus(poNumber);
if ("APPROVED".equals(status.getApprovalStatus())) {
return ApprovalStatus.APPROVED;
}
if ("REJECTED".equals(status.getApprovalStatus())) {
return ApprovalStatus.REJECTED;
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return ApprovalStatus.TIMEOUT;
}
}
return ApprovalStatus.TIMEOUT;
}
}
四、外观模式与相关模式的辨析
外观 vs 适配器:外观简化接口,适配器转换接口。外观面向整个子系统,适配器面向单个对象。外观的动机是简化使用,适配器的动机是解决不兼容。
外观 vs 代理:外观提供简化的高层接口,代理控制对对象的访问。外观可能暴露大量功能,代理通常保持相同接口。外观是"多对一"的简化,代理是"一对一"的控制。
外观 vs 中介者:外观是单向的(客户端通过外观访问子系统),中介者是双向的(同事对象通过中介者相互通信)。外观不添加新行为,中介者封装交互逻辑。
外观 vs 建造者:外观组织已有对象的协作,建造者逐步创建复杂对象。外观是结构型模式,建造者是创建型模式。
五、现代架构中的外观思想
BFF(Backend for Frontend):为不同前端(iOS、Android、Web、小程序)提供定制化的外观层,聚合多个微服务的数据,适配前端特定的展示需求。
GraphQL Gateway:作为多个REST/GRPC服务的统一外观,允许客户端灵活查询所需字段,减少多次往返。
Serverless函数:Lambda/Cloud Function常作为轻量级外观,编排多个云服务的调用。
API Gateway:Kong、Zuul、Spring Cloud Gateway等,作为整个后端集群的外观,处理认证、限流、路由、协议转换。
六、设计考量与最佳实践
外观的粒度控制:过粗的外观成为"上帝对象",过细的外观失去简化价值。建议按业务用例或用户角色划分外观边界。
外观的可测试性:外观层包含协调逻辑,应重点单元测试。使用Mock替代真实子系统,验证调用顺序和参数传递。
外观的性能影响:外观引入额外的间接层,大量数据转换可能影响性能。考虑异步聚合、缓存、流式处理等优化手段。
外观与事务边界:外观常作为事务边界,需审慎选择事务传播行为。长事务可能导致性能问题,考虑Saga模式处理分布式长流程。
七、结语
外观模式是软件设计中最贴近日常工作的模式之一。它不要求高深的算法,不依赖复杂的结构,却能在纷繁复杂的子系统之上,构建出清晰、简洁、可用的交互界面。在微服务化、中台化、遗留系统现代化的浪潮中,外观模式是架构师手中最实用的工具——它保护开发者免受复杂性的侵蚀,让系统的演进既有深度又有温度。掌握外观模式,意味着掌握了在混乱中建立秩序,在复杂中提炼简洁的设计能力。