当前位置: 首页 > news >正文

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

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

什么是适配器模式?

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

适配器模式的三种角色

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

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

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

实际案例:支付系统集成

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

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

// 目标接口:我们期望的统一支付接口
interface PaymentProcessor {fun processPayment(amount: Double, currency: String): PaymentResultfun refund(transactionId: String): Booleanfun 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? = nullval 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.nameitemView.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统一成一致的接口

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

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

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

http://www.dtcms.com/a/544677.html

相关文章:

  • Neo4j中导入.owl数据
  • 应急救援 “眼观六路”:SA/NSA 双模覆盖,偏远灾区也能实时传视频
  • 站长工具短链接生成网站中队人物介绍怎么做
  • 【Spring Boot + Spring Security】从入门到源码精通:藏经阁权限设计与过滤器链深度解析
  • 《嵌入式硬件(十七):基于IMX6ULL的温度传感器LM75a操作》
  • 用 Go 手搓一个内网 DNS 服务器:从此告别 IP 地址,用域名畅游家庭网络!
  • Rust async/await 语法糖的展开原理:从表象到本质
  • Rust 零拷贝技术:从所有权到系统调用的性能优化之道
  • 浪潮服务器装linux系统步骤
  • 视频网站服务器带宽需要多少?视频网站服务器配置要求
  • 《嵌入式硬件(十八):基于IMX6ULL的ADC操作》
  • 注册网站发财的富豪北京公司如何做网站
  • 仓颉语言异常捕获机制深度解析
  • 基于SAP.NET Core Web APP(MVC)的医疗记录管理系统完整开发指南
  • 咖啡网站建设设计规划书wordpress修改首页网址导航
  • C#WPF UI路由事件:事件冒泡与隧道机制
  • 神经网络时序预测融合宏观变量的ETF动态止盈系统设计与实现
  • 分布式Session会话实现方案
  • Java创建【线程池】的方法
  • 相机直播,HDMI线怎么选择
  • 做外贸哪些国外网站可以推广上海中学地址
  • HFSS微带线仿真
  • 推荐常州微信网站建设网站友链怎么做
  • 多模态的大模型文本分类模型代码(二)——模型初步运行
  • 强化特权用户监控,守护Active Directory核心安全
  • Kafka Consumer 消费流程详解
  • 安全守护者:防爆外壳在气体传感器领域的关键应用
  • 【JavaEE初阶】网络经典面试题小小结
  • 以太网多参量传感器:构筑工业安全与环境稳定的“数据堡垒”
  • pinia-storeToRefs方法