适配器与外观模式
大约 11 分钟约 3320 字
适配器与外观模式
简介
适配器模式(Adapter Pattern)和外观模式(Facade Pattern)都是结构型设计模式,但解决不同的问题。适配器模式充当两个不兼容接口之间的桥梁,使它们能够协同工作;外观模式则为复杂的子系统提供一个统一的简化接口。在 C# 开发中,这两种模式经常用于集成第三方库、遗留系统,以及简化复杂的业务逻辑调用。
特点
适配器模式
类适配器(通过继承实现)
// 旧系统的日志接口
public interface ILogger
{
void WriteLog(string message);
void WriteError(string message);
}
// 旧系统的日志实现
public class FileLogger : ILogger
{
public void WriteLog(string message)
{
Console.WriteLine($"[FILE] {DateTime.UtcNow:O} - INFO - {message}");
}
public void WriteError(string message)
{
Console.WriteLine($"[FILE] {DateTime.UtcNow:O} - ERROR - {message}");
}
}
// 新系统要求的日志接口(不同)
public interface INewLogger
{
void LogInformation(string message, params object[] args);
void LogWarning(string message, params object[] args);
void LogError(Exception? ex, string message, params object[] args);
}
// 类适配器 - 继承旧实现,实现新接口
public class LoggerAdapter : FileLogger, INewLogger
{
public void LogInformation(string message, params object[] args)
{
WriteLog(string.Format(message, args));
}
public void LogWarning(string message, params object[] args)
{
WriteLog($"[WARN] {string.Format(message, args)}");
}
public void LogError(Exception? ex, string message, params object[] args)
{
var formattedMessage = string.Format(message, args);
if (ex != null)
WriteError($"{formattedMessage} - Exception: {ex.Message}");
else
WriteError(formattedMessage);
}
}对象适配器(通过组合实现)
// 第三方支付 API 的响应格式
public class ThirdPartyPaymentResponse
{
public string RespCode { get; set; }
public string RespMsg { get; set; }
public string TradeNo { get; set; }
public string TotalAmount { get; set; }
}
// 第三方支付 API 客户端
public class ThirdPartyPaymentClient
{
public ThirdPartyPaymentResponse CreatePayment(string merchantId, decimal amount, string orderId)
{
Console.WriteLine($"[第三方支付] 商户: {merchantId}, 订单: {orderId}, 金额: {amount}");
return new ThirdPartyPaymentResponse
{
RespCode = "00",
RespMsg = "Success",
TradeNo = $"TP{DateTime.UtcNow:yyyyMMddHHmmss}",
TotalAmount = amount.ToString("F2")
};
}
}
// 我们系统的支付接口
public interface IPaymentProcessor
{
Task<PaymentResult> ProcessPaymentAsync(PaymentRequest request);
}
public record PaymentRequest(string OrderId, decimal Amount, string Currency);
public record PaymentResult(bool Success, string TransactionId, string Message);
// 对象适配器 - 将第三方 API 适配到我们的接口
public class ThirdPartyPaymentAdapter : IPaymentProcessor
{
private readonly ThirdPartyPaymentClient _client;
private readonly string _merchantId;
public ThirdPartyPaymentAdapter(ThirdPartyPaymentClient client, string merchantId)
{
_client = client;
_merchantId = merchantId;
}
public Task<PaymentResult> ProcessPaymentAsync(PaymentRequest request)
{
// 适配:将我们的请求格式转换为第三方 API 的格式
var response = _client.CreatePayment(
_merchantId,
request.Amount,
request.OrderId);
// 适配:将第三方响应转换为我们系统的格式
var result = new PaymentResult(
Success: response.RespCode == "00",
TransactionId: response.TradeNo,
Message: response.RespMsg);
return Task.FromResult(result);
}
}
// 另一个第三方支付的适配器(不同的第三方)
public class StripePaymentAdapter : IPaymentProcessor
{
private readonly HttpClient _httpClient;
public StripePaymentAdapter(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<PaymentResult> ProcessPaymentAsync(PaymentRequest request)
{
// Stripe API 的调用和转换逻辑
var payload = new
{
amount = (long)(request.Amount * 100), // Stripe 用分为单位
currency = request.Currency.ToLower(),
metadata = new { order_id = request.OrderId }
};
Console.WriteLine($"[Stripe] 处理支付: {payload}");
await Task.Delay(100); // 模拟 API 调用
return new PaymentResult(true, $"ch_{Guid.NewGuid():N}", "Payment succeeded");
}
}多适配器统一管理
// 适配器工厂 - 根据配置选择支付适配器
public class PaymentProcessorFactory
{
private readonly IServiceProvider _serviceProvider;
private readonly IConfiguration _configuration;
public PaymentProcessorFactory(IServiceProvider serviceProvider, IConfiguration configuration)
{
_serviceProvider = serviceProvider;
_configuration = configuration;
}
public IPaymentProcessor Create()
{
var provider = _configuration["Payment:Provider"];
return provider switch
{
"ThirdParty" => new ThirdPartyPaymentAdapter(
new ThirdPartyPaymentClient(),
_configuration["Payment:MerchantId"]!),
"Stripe" => new StripePaymentAdapter(
_serviceProvider.GetRequiredService<HttpClient>()),
_ => throw new InvalidOperationException($"不支持的支付提供商: {provider}")
};
}
}外观模式
复杂子系统示例
// 子系统 1:库存管理
public class InventorySystem
{
public bool CheckStock(string productId, int quantity)
{
Console.WriteLine($"[库存] 检查商品 {productId} 库存是否充足(需要 {quantity} 件)");
return true;
}
public void ReserveStock(string productId, int quantity)
{
Console.WriteLine($"[库存] 预留 {quantity} 件商品 {productId}");
}
public void ReleaseStock(string productId, int quantity)
{
Console.WriteLine($"[库存] 释放 {quantity} 件商品 {productId}");
}
}
// 子系统 2:支付处理
public class PaymentSystem
{
public bool Charge(string accountId, decimal amount)
{
Console.WriteLine($"[支付] 从账户 {accountId} 扣款 {amount:C}");
return true;
}
public void Refund(string accountId, decimal amount)
{
Console.WriteLine($"[支付] 向账户 {accountId} 退款 {amount:C}");
}
}
// 子系统 3:物流配送
public class ShippingSystem
{
public string CreateShipment(string orderId, string address)
{
var trackingNumber = $"SF{Guid.NewGuid():N}"[..15];
Console.WriteLine($"[物流] 创建配送单: {trackingNumber},目的地: {address}");
return trackingNumber;
}
public void CancelShipment(string trackingNumber)
{
Console.WriteLine($"[物流] 取消配送单: {trackingNumber}");
}
}
// 子系统 4:通知服务
public class NotificationSystem
{
public void SendOrderConfirmation(string email, string orderId, decimal amount)
{
Console.WriteLine($"[通知] 发送订单确认邮件给 {email},订单号: {orderId},金额: {amount:C}");
}
public void SendShippingNotification(string email, string trackingNumber)
{
Console.WriteLine($"[通知] 发送发货通知给 {email},快递单号: {trackingNumber}");
}
}
// 子系统 5:积分系统
public class PointsSystem
{
public void AwardPoints(string userId, decimal orderAmount)
{
var points = (int)(orderAmount * 10);
Console.WriteLine($"[积分] 为用户 {userId} 发放 {points} 积分");
}
public void DeductPoints(string userId, decimal orderAmount)
{
var points = (int)(orderAmount * 10);
Console.WriteLine($"[积分] 扣除用户 {userId} 的 {points} 积分");
}
}外观类 - 简化子系统交互
// 外观类 - 提供简化的订单处理接口
public class OrderFacade
{
private readonly InventorySystem _inventory;
private readonly PaymentSystem _payment;
private readonly ShippingSystem _shipping;
private readonly NotificationSystem _notification;
private readonly PointsSystem _points;
public OrderFacade()
{
_inventory = new InventorySystem();
_payment = new PaymentSystem();
_shipping = new ShippingSystem();
_notification = new NotificationSystem();
_points = new PointsSystem();
}
// 客户端只需要调用一个方法
public OrderResult PlaceOrder(Order order)
{
Console.WriteLine($"========== 开始处理订单 {order.OrderId} ==========");
// 1. 检查库存
if (!_inventory.CheckStock(order.ProductId, order.Quantity))
return new OrderResult(false, "库存不足");
try
{
// 2. 预留库存
_inventory.ReserveStock(order.ProductId, order.Quantity);
// 3. 扣款
if (!_payment.Charge(order.AccountId, order.TotalAmount))
{
_inventory.ReleaseStock(order.ProductId, order.Quantity);
return new OrderResult(false, "支付失败");
}
// 4. 创建配送单
var trackingNumber = _shipping.CreateShipment(order.OrderId, order.Address);
// 5. 发放积分
_points.AwardPoints(order.UserId, order.TotalAmount);
// 6. 发送通知
_notification.SendOrderConfirmation(order.Email, order.OrderId, order.TotalAmount);
_notification.SendShippingNotification(order.Email, trackingNumber);
Console.WriteLine($"========== 订单 {order.OrderId} 处理完成 ==========");
return new OrderResult(true, "订单处理成功", trackingNumber);
}
catch (Exception ex)
{
// 出错时回滚所有操作
_inventory.ReleaseStock(order.ProductId, order.Quantity);
_payment.Refund(order.AccountId, order.TotalAmount);
return new OrderResult(false, $"处理失败: {ex.Message}");
}
}
public void CancelOrder(Order order, string trackingNumber)
{
_inventory.ReleaseStock(order.ProductId, order.Quantity);
_payment.Refund(order.AccountId, order.TotalAmount);
_shipping.CancelShipment(trackingNumber);
_points.DeductPoints(order.UserId, order.TotalAmount);
Console.WriteLine($"订单 {order.OrderId} 已取消");
}
}
public record Order(
string OrderId, string ProductId, int Quantity,
decimal TotalAmount, string AccountId, string UserId,
string Email, string Address);
public record OrderResult(bool Success, string Message, string? TrackingNumber = null);
// 客户端使用 - 非常简单
var facade = new OrderFacade();
var order = new Order(
"ORD-001", "PROD-100", 2,
599.98m, "ACC-001", "USER-001",
"customer@example.com", "北京市朝阳区");
var result = facade.PlaceOrder(order);
Console.WriteLine($"结果: {result.Message}");在 ASP.NET Core 中使用外观
// 注册为 Scoped 服务
builder.Services.AddScoped<OrderFacade>();
[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
private readonly OrderFacade _orderFacade;
public OrdersController(OrderFacade orderFacade)
{
_orderFacade = orderFacade;
}
[HttpPost]
public IActionResult PlaceOrder([FromBody] PlaceOrderRequest request)
{
var order = new Order(
Guid.NewGuid().ToString("N")[..8],
request.ProductId,
request.Quantity,
request.TotalAmount,
request.AccountId,
User.FindFirst("sub")?.Value ?? "anonymous",
request.Email,
request.Address);
var result = _orderFacade.PlaceOrder(order);
return result.Success ? Ok(result) : BadRequest(result);
}
}优点
缺点
适配器模式进阶
类适配器 vs 对象适配器对比
// ========== 类适配器 ==========
// 通过多重继承实现(C# 中通过继承 + 接口实现)
// 优点:可以重用父类的实现
// 缺点:耦合度高,C# 不支持多重类继承
// ========== 对象适配器 ==========
// 通过组合实现(持有被适配对象的引用)
// 优点:松耦合,符合组合优于继承原则
// 缺点:需要创建被适配对象
// 推荐优先使用对象适配器(组合优于继承)双向适配器
/// <summary>
/// 双向适配器 — 同时支持新旧接口互相调用
/// </summary>
public interface ILegacySystem
{
string GetData(string query);
void SaveData(string data);
}
public interface IModernSystem
{
Task<string> QueryAsync(string endpoint, Dictionary<string, string> parameters);
Task<bool> UpdateAsync(string endpoint, object payload);
}
public class BidirectionalAdapter : ILegacySystem, IModernSystem
{
private readonly IModernSystem _modern;
private readonly ILegacySystem _legacy;
// 旧系统客户端 → 适配 → 调用新系统
public BidirectionalAdapter(IModernSystem modern)
{
_modern = modern;
}
// 新系统客户端 → 适配 → 调用旧系统
public BidirectionalAdapter(ILegacySystem legacy)
{
_legacy = legacy;
}
// ILegacySystem 实现(内部调用新系统)
public string GetData(string query)
{
return _modern.QueryAsync("/api/data", new Dictionary<string, string> { ["q"] = query })
.GetAwaiter().GetResult();
}
public void SaveData(string data)
{
_modern.UpdateAsync("/api/data", new { content = data }).GetAwaiter().GetResult();
}
// IModernSystem 实现(内部调用旧系统)
public Task<string> QueryAsync(string endpoint, Dictionary<string, string> parameters)
{
var query = string.Join("&", parameters.Select(kv => $"{kv.Key}={kv.Value}"));
return Task.FromResult(_legacy.GetData(query));
}
public Task<bool> UpdateAsync(string endpoint, object payload)
{
_legacy.SaveData(System.Text.Json.JsonSerializer.Serialize(payload));
return Task.FromResult(true);
}
}缓存适配器
/// <summary>
/// 缓存适配器 — 统一不同缓存提供者的接口
/// </summary>
public interface ICacheProvider
{
Task<T?> GetAsync<T>(string key);
Task SetAsync<T>(string key, T value, TimeSpan? expiry = null);
Task RemoveAsync(string key);
Task<bool> ExistsAsync(string key);
}
// Redis 适配器
public class RedisCacheAdapter : ICacheProvider
{
private readonly ConnectionMultiplexer _redis;
private readonly IDatabase _db;
public RedisCacheAdapter(string connectionString)
{
_redis = ConnectionMultiplexer.Connect(connectionString);
_db = _redis.GetDatabase();
}
public async Task<T?> GetAsync<T>(string key)
{
var value = await _db.StringGetAsync(key);
return value.HasValue ? JsonSerializer.Deserialize<T>(value!) : default;
}
public async Task SetAsync<T>(string key, T value, TimeSpan? expiry = null)
{
var json = JsonSerializer.Serialize(value);
await _db.StringSetAsync(key, json, expiry);
}
public async Task RemoveAsync(string key) => await _db.KeyDeleteAsync(key);
public async Task<bool> ExistsAsync(string key) => await _db.KeyExistsAsync(key);
}
// 内存缓存适配器
public class MemoryCacheAdapter : ICacheProvider
{
private readonly IMemoryCache _cache;
public MemoryCacheAdapter(IMemoryCache cache) => _cache = cache;
public Task<T?> GetAsync<T>(string key)
{
_cache.TryGetValue(key, out T? value);
return Task.FromResult(value);
}
public Task SetAsync<T>(string key, T value, TimeSpan? expiry = null)
{
var options = expiry.HasValue
? new MemoryCacheEntryOptions { AbsoluteExpirationRelativeToNow = expiry }
: new MemoryCacheEntryOptions();
_cache.Set(key, value, options);
return Task.CompletedTask;
}
public Task RemoveAsync(string key) { _cache.Remove(key); return Task.CompletedTask; }
public Task<bool> ExistsAsync(string key) => Task.FromResult(_cache.TryGetValue(key, out _));
}
// 通过 DI 注册不同的适配器
// builder.Services.AddSingleton<ICacheProvider, RedisCacheAdapter>();
// 或
// builder.Services.AddSingleton<ICacheProvider, MemoryCacheAdapter>();外观模式进阶
分层外观
/// <summary>
/// 分层外观 — 为不同层级的客户端提供不同粒度的接口
/// </summary>
// 底层外观 — 面向开发者,提供细粒度控制
public class OrderProcessingFacade
{
private readonly InventorySystem _inventory;
private readonly PaymentSystem _payment;
public OrderProcessingFacade(InventorySystem inventory, PaymentSystem payment)
{
_inventory = inventory;
_payment = payment;
}
public bool CheckAndReserve(string productId, int quantity)
{
if (!_inventory.CheckStock(productId, quantity)) return false;
_inventory.ReserveStock(productId, quantity);
return true;
}
public bool ProcessPayment(string accountId, decimal amount)
{
return _payment.Charge(accountId, amount);
}
public void Rollback(string productId, int quantity, string accountId, decimal amount)
{
_inventory.ReleaseStock(productId, quantity);
_payment.Refund(accountId, amount);
}
}
// 高层外观 — 面向业务,提供一站式操作
public class BusinessOrderFacade
{
private readonly OrderProcessingFacade _processing;
private readonly ShippingSystem _shipping;
private readonly NotificationSystem _notification;
public OrderResult QuickOrder(Order order)
{
// 高层外观封装所有细节
if (!_processing.CheckAndReserve(order.ProductId, order.Quantity))
return new OrderResult(false, "库存不足");
if (!_processing.ProcessPayment(order.AccountId, order.TotalAmount))
{
_processing.Rollback(order.ProductId, order.Quantity, order.AccountId, 0);
return new OrderResult(false, "支付失败");
}
var tracking = _shipping.CreateShipment(order.OrderId, order.Address);
_notification.SendOrderConfirmation(order.Email, order.OrderId, order.TotalAmount);
return new OrderResult(true, "下单成功", tracking);
}
}总结
适配器模式和外观模式都是处理接口不匹配问题的有效手段。适配器模式主要用于让两个不兼容的接口协同工作,常见于第三方库集成和遗留系统对接;外观模式用于简化复杂子系统的使用,提供一个更高层的统一接口。在实际开发中,两者经常配合使用:先用外观模式简化复杂子系统,再用适配器模式桥接不同的接口。在 ASP.NET Core 中,这两种模式可以通过依赖注入优雅地集成到应用架构中。
关键知识点
- 模式不是目标,降低耦合和控制变化才是目标。
- 先找变化点、稳定点和协作边界,再决定是否引入模式。
- 同一个模式在不同规模下的收益和代价差异很大。
项目落地视角
- 优先画出参与对象、依赖方向和调用链,再落到代码。
- 把模式放到一个真实场景里,比如支付、规则引擎、工作流或插件扩展。
- 配合单元测试或契约测试,保证重构后的行为没有漂移。
常见误区
- 为了看起来“高级”而套模式。
- 把简单问题拆成过多抽象层,导致阅读和排障都变难。
- 只会背 UML,不会解释为什么这里需要这个模式。
进阶路线
- 继续关注模式之间的组合用法,而不是孤立记忆。
- 从业务建模、演进策略和团队协作角度看模式的适用性。
- 把模式结论沉淀为项目模板、基类或约束文档。
适用场景
- 当你准备把《适配器与外观模式》真正落到项目里时,最适合先在一个独立模块或最小样例里验证关键路径。
- 适合在业务规则频繁变化、分支增多或对象协作复杂时引入。
- 当你希望提高扩展性,但又不想把系统拆得过度抽象时,这类主题很有参考价值。
落地建议
- 先识别变化点,再决定是否引入模式,而不是反过来套模板。
- 优先为模式的边界、依赖和调用路径画出简单结构图。
- 把模式落到一个明确场景,例如支付、规则计算、插件扩展或工作流。
排错清单
- 检查抽象层是否过多,导致调用路径和责任不清晰。
- 确认引入模式后是否真的减少了条件分支和重复代码。
- 警惕“为了模式而模式”,尤其是在简单业务里。
复盘问题
- 如果把《适配器与外观模式》放进你的当前项目,最先要验证的输入、输出和失败路径分别是什么?
- 《适配器与外观模式》最容易在什么规模、什么边界条件下暴露问题?你会用什么指标或日志去确认?
- 相比默认实现或替代方案,采用《适配器与外观模式》最大的收益和代价分别是什么?
