适配器模式:让不兼容的接口协同工作

在软件设计中,我们经常会遇到系统整合的问题:新系统需要与老系统对接,第三方库的接口与我们的期望不匹配,或者不同模块之间的接口标准不一致。适配器模式正是解决这类接口不兼容问题的银弹。

什么是适配器模式?

适配器模式(Adapter Pattern)是一种结构型设计模式,它充当两个不兼容接口之间的桥梁,将一个类的接口转换成客户端期望的另一个接口。就像现实世界中的电源适配器一样,它让美规插头能在中国的插座上使用。

适配器模式的三种角色

  • 目标接口(Target)​​:客户端期望的接口

  • 适配者(Adaptee)​​:需要被适配的现有接口

  • 适配器(Adapter)​​:将适配者接口转换成目标接口

实际案例:支付系统集成

假设我们正在开发一个电商平台,需要集成多种支付方式,但每个支付服务商提供的接口都不相同。

1. 定义统一的支付接口(目标接口)

复制代码
// 目标接口:我们期望的统一支付接口
interface PaymentProcessor {
    fun processPayment(amount: Double, currency: String): PaymentResult
    fun refund(transactionId: String): Boolean
    fun supports(currency: String): Boolean
}

data class PaymentResult(
    val success: Boolean,
    val transactionId: String? = null,
    val message: String = ""
)

2. 现有的支付服务(需要被适配的类)

复制代码
// 支付宝SDK提供的类(我们无法修改)
class AlipaySDK {
    fun pay(amount: Double, currency: String, callback: (Boolean, String) -> Unit) {
        // 支付宝的异步回调方式
        Thread.sleep(1000) // 模拟网络请求
        callback(true, "ALIPAY_${System.currentTimeMillis()}")
    }
    
    fun isCurrencySupported(currency: String): Boolean {
        return currency in listOf("CNY", "USD", "EUR")
    }
}

// 微信支付SDK
class WechatPaySDK {
    fun wechatPayment(amount: Double, curr: String): WechatResponse {
        Thread.sleep(800)
        return WechatResponse(
            code = 200,
            transactionNo = "WECHAT_${System.currentTimeMillis()}",
            msg = "支付成功"
        )
    }
}

data class WechatResponse(val code: Int, val transactionNo: String, val msg: String)

// PayPal SDK(国外支付)
class PayPalService {
    fun makePayment(amount: Double, currency: String): PaymentOutcome {
        Thread.sleep(1200)
        return if (amount > 0) {
            PaymentOutcome("SUCCESS", "PAYPAL_${System.currentTimeMillis()}")
        } else {
            PaymentOutcome("FAILED", null)
        }
    }
    
    fun processRefund(paymentId: String): Boolean {
        return paymentId.startsWith("PAYPAL")
    }
}

data class PaymentOutcome(val status: String, val paymentId: String?)

3. 实现适配器类

复制代码
// 支付宝适配器
class AlipayAdapter(private val alipay: AlipaySDK) : PaymentProcessor {
    
    override fun processPayment(amount: Double, currency: String): PaymentResult {
        return try {
            var result: PaymentResult? = null
            val lock = Object()
            
            alipay.pay(amount, currency) { success, transactionId ->
                result = if (success) {
                    PaymentResult(true, transactionId, "支付宝支付成功")
                } else {
                    PaymentResult(false, message = "支付宝支付失败")
                }
                synchronized(lock) { lock.notify() }
            }
            
            synchronized(lock) { lock.wait(5000) } // 等待回调,超时5秒
            
            result ?: PaymentResult(false, message = "支付超时")
        } catch (e: Exception) {
            PaymentResult(false, message = "支付宝支付异常: ${e.message}")
        }
    }
    
    override fun refund(transactionId: String): Boolean {
        // 支付宝退款逻辑(简化)
        return transactionId.startsWith("ALIPAY")
    }
    
    override fun supports(currency: String): Boolean {
        return alipay.isCurrencySupported(currency)
    }
}

// 微信支付适配器
class WechatPayAdapter(private val wechatPay: WechatPaySDK) : PaymentProcessor {
    
    override fun processPayment(amount: Double, currency: String): PaymentResult {
        return try {
            val response = wechatPay.wechatPayment(amount, currency)
            if (response.code == 200) {
                PaymentResult(true, response.transactionNo, response.msg)
            } else {
                PaymentResult(false, message = "微信支付失败: ${response.msg}")
            }
        } catch (e: Exception) {
            PaymentResult(false, message = "微信支付异常: ${e.message}")
        }
    }
    
    override fun refund(transactionId: String): Boolean {
        // 微信退款逻辑
        return transactionId.startsWith("WECHAT")
    }
    
    override fun supports(currency: String): Boolean {
        // 微信支付支持常见货币
        return currency in listOf("CNY", "USD", "HKD")
    }
}

// PayPal适配器
class PayPalAdapter(private val paypal: PayPalService) : PaymentProcessor {
    
    override fun processPayment(amount: Double, currency: String): PaymentResult {
        return try {
            val outcome = paypal.makePayment(amount, currency)
            when (outcome.status) {
                "SUCCESS" -> PaymentResult(true, outcome.paymentId, "PayPal支付成功")
                else -> PaymentResult(false, message = "PayPal支付失败")
            }
        } catch (e: Exception) {
            PaymentResult(false, message = "PayPal支付异常: ${e.message}")
        }
    }
    
    override fun refund(transactionId: String): Boolean {
        return paypal.processRefund(transactionId)
    }
    
    override fun supports(currency: String): Boolean {
        // PayPal支持全球主要货币
        return currency in listOf("USD", "EUR", "GBP", "CAD", "AUD", "JPY", "CNY")
    }
}

4. 支付工厂类

复制代码
// 支付工厂,统一创建适配器
object PaymentProcessorFactory {
    fun createProcessor(type: PaymentType): PaymentProcessor {
        return when (type) {
            PaymentType.ALIPAY -> AlipayAdapter(AlipaySDK())
            PaymentType.WECHAT -> WechatPayAdapter(WechatPaySDK())
            PaymentType.PAYPAL -> PayPalAdapter(PayPalService())
        }
    }
}

enum class PaymentType {
    ALIPAY, WECHAT, PAYPAL
}

5. 客户端使用

复制代码
// 电商订单处理类
class OrderService {
    private val paymentProcessors = mutableMapOf<PaymentType, PaymentProcessor>()
    
    fun processOrder(order: Order) {
        println("处理订单: ${order.id}, 金额: ${order.amount} ${order.currency}")
        
        val processor = getPaymentProcessor(order.paymentType, order.currency)
            ?: throw IllegalArgumentException("不支持的支付方式或货币")
        
        val result = processor.processPayment(order.amount, order.currency)
        
        if (result.success) {
            println("✅ 支付成功! 交易号: ${result.transactionId}")
            // 更新订单状态
            updateOrderStatus(order.id, OrderStatus.PAID, result.transactionId)
        } else {
            println("❌ 支付失败: ${result.message}")
            updateOrderStatus(order.id, OrderStatus.FAILED)
        }
    }
    
    private fun getPaymentProcessor(type: PaymentType, currency: String): PaymentProcessor? {
        val processor = paymentProcessors.getOrPut(type) {
            PaymentProcessorFactory.createProcessor(type)
        }
        return if (processor.supports(currency)) processor else null
    }
    
    fun refundOrder(order: Order) {
        order.transactionId?.let { transactionId ->
            val processor = PaymentProcessorFactory.createProcessor(order.paymentType)
            val success = processor.refund(transactionId)
            
            if (success) {
                println("✅ 退款成功")
                updateOrderStatus(order.id, OrderStatus.REFUNDED)
            } else {
                println("❌ 退款失败")
            }
        }
    }
    
    private fun updateOrderStatus(orderId: String, status: OrderStatus, transactionId: String? = null) {
        // 实际项目中会更新数据库
        println("更新订单状态: $orderId -> $status")
    }
}

// 数据类
data class Order(
    val id: String,
    val amount: Double,
    val currency: String,
    val paymentType: PaymentType,
    val transactionId: String? = null
)

enum class OrderStatus {
    PENDING, PAID, FAILED, REFUNDED
}

6. 测试代码

复制代码
fun main() {
    val orderService = OrderService()
    
    // 测试不同支付方式
    val orders = listOf(
        Order("ORDER_001", 299.0, "CNY", PaymentType.ALIPAY),
        Order("ORDER_002", 199.0, "USD", PaymentType.PAYPAL),
        Order("ORDER_003", 159.0, "CNY", PaymentType.WECHAT),
        Order("ORDER_004", 399.0, "EUR", PaymentType.ALIPAY) // 测试不支持的货币
    )
    
    orders.forEach { order ->
        println("\n" + "=".repeat(50))
        try {
            orderService.processOrder(order)
        } catch (e: Exception) {
            println("❌ 订单处理异常: ${e.message}")
        }
        Thread.sleep(1000) // 模拟处理间隔
    }
    
    // 测试退款
    println("\n" + "=".repeat(50))
    val refundOrder = Order("ORDER_001", 299.0, "CNY", PaymentType.ALIPAY, "ALIPAY_123456")
    orderService.refundOrder(refundOrder)
}

适配器模式的变体

1. 类适配器(通过继承)

复制代码
// 类适配器(Kotlin 支持多接口继承)
open class LegacyLogger {
    fun logMessage(level: String, message: String) {
        println("[$level] $message")
    }
}

interface ModernLogger {
    fun debug(message: String)
    fun info(message: String)
    fun error(message: String)
}

// 类适配器通过继承实现
class LoggerAdapter : LegacyLogger(), ModernLogger {
    override fun debug(message: String) = logMessage("DEBUG", message)
    override fun info(message: String) = logMessage("INFO", message)
    override fun error(message: String) = logMessage("ERROR", message)
}

2. 对象适配器(通过组合)

复制代码
// 对象适配器(推荐,符合组合优于继承原则)
class LoggerAdapter(private val legacyLogger: LegacyLogger) : ModernLogger {
    override fun debug(message: String) = legacyLogger.logMessage("DEBUG", message)
    override fun info(message: String) = legacyLogger.logMessage("INFO", message)
    override fun error(message: String) = legacyLogger.logMessage("ERROR", message)
}

适配器模式在Android开发中的应用

1. RecyclerView适配器

复制代码
// 经典的Adapter模式应用
class UserAdapter(private val users: List<User>) : RecyclerView.Adapter<UserAdapter.ViewHolder>() {
    
    // 将数据适配为View的展示
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val user = users[position]
        holder.bind(user)
    }
    
    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        fun bind(user: User) {
            itemView.usernameText.text = user.name
            itemView.emailText.text = user.email
            // 将数据适配到UI组件
        }
    }
}

2. 第三方库适配

复制代码
// 假设我们需要将不同的图片加载库统一接口
interface ImageLoader {
    fun loadImage(url: String, imageView: ImageView)
    fun loadImageWithPlaceholder(url: String, imageView: ImageView, placeholder: Int)
}

// Glide适配器
class GlideAdapter : ImageLoader {
    override fun loadImage(url: String, imageView: ImageView) {
        Glide.with(imageView.context)
            .load(url)
            .into(imageView)
    }
    
    override fun loadImageWithPlaceholder(url: String, imageView: ImageView, placeholder: Int) {
        Glide.with(imageView.context)
            .load(url)
            .placeholder(placeholder)
            .into(imageView)
    }
}

// Picasso适配器
class PicassoAdapter : ImageLoader {
    override fun loadImage(url: String, imageView: ImageView) {
        Picasso.get().load(url).into(imageView)
    }
    
    override fun loadImageWithPlaceholder(url: String, imageView: ImageView, placeholder: Int) {
        Picasso.get().load(url).placeholder(placeholder).into(imageView)
    }
}

适配器模式的优缺点

✅ 优点

  1. 单一职责原则​:将接口转换代码从业务逻辑中分离

  2. 开闭原则​:可以引入新适配器而不修改现有代码

  3. 解耦合​:客户端与具体实现解耦

  4. 复用性​:可以复用现有的类

❌ 缺点

  1. 复杂度增加​:需要增加额外的类和接口

  2. 过度设计​:在简单场景下可能显得繁琐

适用场景

  1. 系统集成​:整合第三方库或遗留系统

  2. 接口标准化​:统一多个类似功能的接口

  3. 版本兼容​:新版本API需要兼容老版本

  4. 测试模拟​:创建测试替身(Test Double)

最佳实践

  1. 优先使用对象适配器​:组合优于继承,更灵活

  2. 保持适配器简单​:只做接口转换,不添加业务逻辑

  3. 使用依赖注入​:便于测试和替换不同的适配器

  4. 适当的命名 ​:如XxxAdapterXxxWrapper

总结

适配器模式是解决接口不兼容问题的利器,它就像软件世界中的"万能转接头"。通过本文的支付系统案例,我们可以看到适配器模式在实际项目中的强大作用:

  • 🎯 ​统一接口​:将不同的支付SDK统一成一致的接口

  • 🔄 ​灵活扩展​:新增支付方式只需添加新的适配器

  • 🧪 ​易于测试​:可以轻松创建模拟适配器进行单元测试

  • 🏗️ ​结构清晰​:代码职责分明,维护性强

相关推荐
ljh_learn_from_base7 小时前
【spring boot 使用apache poi 生成和处理word 文档】
java·spring boot·word·apache
涔溪7 小时前
在 Electron 框架中实现数据库的连接、读取和写入
javascript·数据库·electron
锈儿海老师7 小时前
超越平台:Vercel 的野心是定义编程语言的未来吗?
前端·javascript·架构
数字芯片实验室8 小时前
流片可以失败,但人心的账本不能亏空
java·开发语言
华仔啊8 小时前
为什么你的 @Transactional 不生效?一文搞懂 Spring 事务机制
java·后端
Lacrimosa&L8 小时前
OS_3 Memory、4 File、5 IO
java
爱学的小码8 小时前
JavaEE——多线程1(超详细版)
java·java-ee
tuokuac8 小时前
依赖spring-cloud-starter-gateway与spring-cloud-gateway-dependencies的区别
java·gateway
seabirdssss8 小时前
JDK 11 环境正确,端口未被占用,但是运行 Tomcat 11 仍然闪退
java·开发语言·tomcat