适配器模式:真实案例解析

发布: (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);
}
Back to Blog

相关文章

阅读更多 »