适配器模式:真实案例解析
发布: (2025年12月7日 GMT+8 20:12)
6 min read
原文: Dev.to
Source: Dev.to
介绍
你正在把第三方支付网关集成到你的应用中。一切看起来都很直截了当,直到你发现它们的 SDK 使用的接口与你的代码库期望的完全不同。听起来熟悉吗?这正是 适配器模式 发挥作用的地方。
适配器模式是四人帮(Gang of Four)中最实用的设计模式之一。它充当两个不兼容接口之间的桥梁,使本来无法协同工作的类能够一起工作。可以把它想象成出国旅行时的电源适配器——你的设备工作方式不变,适配器负责处理不兼容的插座。
在本文中,我们将通过真实的 .NET 示例来演示适配器模式,这些示例你在生产代码中真的会遇到:支付网关、日志系统以及遗留代码的集成。
什么是适配器模式?
适配器模式将一个类的接口转换成客户端期望的另一个接口。它使本来因为接口不兼容而无法一起工作的类能够协同工作。
关键组成部分
- 目标接口(Target Interface) – 客户端代码期望使用的接口。
- 被适配者(Adaptee) – 具有不兼容接口的已有类。
- 适配器(Adapter) – 在目标接口和被适配者之间搭建桥梁的类。
- 客户端(Client) – 使用目标接口的代码。
实例 1:支付网关集成
目标接口(你的应用期望的接口)
// Your application's payment interface
public interface IPaymentProcessor
{
Task ProcessPaymentAsync(
decimal amount,
string currency,
string cardToken);
Task RefundAsync(string transactionId, decimal amount);
}
public record PaymentResult(
bool Success,
string TransactionId,
string? ErrorMessage);
public record RefundResult(
bool Success,
string RefundId,
string? ErrorMessage);
被适配者(Stripe 的 SDK – 简化版)
// Stripe's SDK has its own conventions
public class StripeClient
{
public async Task CreateChargeAsync(StripeChargeRequest request)
{
// Simulate API call
await Task.Delay(100);
return new StripeCharge
{
Id = $"ch_{Guid.NewGuid():N}",
Status = "succeeded",
Amount = request.AmountInCents
};
}
public async Task CreateRefundAsync(string chargeId, long amountInCents)
{
await Task.Delay(100);
return new StripeRefund
{
Id = $"re_{Guid.NewGuid():N}",
Status = "succeeded"
};
}
}
// Stripe uses cents, not decimal amounts!
public class StripeChargeRequest
{
public long AmountInCents { get; set; }
public string Currency { get; set; } = "usd";
public string Source { get; set; } = string.Empty; // Card token
}
public class StripeCharge
{
public string Id { get; set; } = string.Empty;
public string Status { get; set; } = string.Empty;
public long Amount { get; set; }
}
public class StripeRefund
{
public string Id { get; set; } = string.Empty;
public string Status { get; set; } = string.Empty;
}
适配器(桥接两者)
public class StripePaymentAdapter : IPaymentProcessor
{
private readonly StripeClient _stripeClient;
private readonly ILogger _logger;
public StripePaymentAdapter(
StripeClient stripeClient,
ILogger logger)
{
_stripeClient = stripeClient;
_logger = logger;
}
public async Task ProcessPaymentAsync(
decimal amount,
string currency,
string cardToken)
{
try
{
var request = new StripeChargeRequest
{
AmountInCents = (long)(amount * 100),
Currency = currency.ToLowerInvariant(),
Source = cardToken
};
var charge = await _stripeClient.CreateChargeAsync(request);
return new PaymentResult(
Success: charge.Status == "succeeded",
TransactionId: charge.Id,
ErrorMessage: null);
}
catch (Exception ex)
{
_logger.LogError(ex, "Stripe payment failed");
return new PaymentResult(
Success: false,
TransactionId: string.Empty,
ErrorMessage: ex.Message);
}
}
public async Task RefundAsync(string transactionId, decimal amount)
{
try
{
var amountInCents = (long)(amount * 100);
var refund = await _stripeClient.CreateRefundAsync(transactionId, amountInCents);
return new RefundResult(
Success: refund.Status == "succeeded",
RefundId: refund.Id,
ErrorMessage: null);
}
catch (Exception ex)
{
_logger.LogError(ex, "Stripe refund failed");
return new RefundResult(false, string.Empty, ex.Message);
}
}
}
实例 2:日志系统适配器
目标接口
public interface IAppLogger
{
void LogInfo(string message, params object[] args);
void LogWarning(string message, params object[] args);
void LogError(Exception ex, string message, params object[] args);
void LogDebug(string message, params object[] args);
}
被适配者(不同的日志库)
// Serilog‑style logger
public class SerilogLogger
{
public void Write(LogLevel level, string template, params object[] values) { }
public void Write(LogLevel level, Exception ex, string template, params object[] values) { }
}
// Legacy logging library
public class LegacyLogger
{
public void WriteToLog(string category, string message, int severity) { }
public void WriteException(string category, Exception ex) { }
}
适配器
public class SerilogAdapter : IAppLogger
{
private readonly SerilogLogger _logger;
public SerilogAdapter(SerilogLogger logger) => _logger = logger;
public void LogInfo(string message, params object[] args)
=> _logger.Write(LogLevel.Information, message, args);
public void LogWarning(string message, params object[] args)
=> _logger.Write(LogLevel.Warning, message, args);
public void LogError(Exception ex, string message, params object[] args)
=> _logger.Write(LogLevel.Error, ex, message, args);
public void LogDebug(string message, params object[] args)
=> _logger.Write(LogLevel.Debug, message, args);
}
public class LegacyLoggerAdapter : IAppLogger
{
private readonly LegacyLogger _logger;
private readonly string _category;
public LegacyLoggerAdapter(LegacyLogger logger, string category = "Application")
{
_logger = logger;
_category = category;
}
public void LogInfo(string message, params object[] args)
=> _logger.WriteToLog(_category, string.Format(message, args), severity: 1);
public void LogWarning(string message, params object[] args)
=> _logger.WriteToLog(_category, string.Format(message, args), severity: 2);
public void LogError(Exception ex, string message, params object[] args)
{
_logger.WriteToLog(_category, string.Format(message, args), severity: 3);
_logger.WriteException(_category, ex);
}
public void LogDebug(string message, params object[] args)
=> _logger.WriteToLog(_category, string.Format(message, args), severity: 0);
}