在Golang项目开发过程中,对接不同格式的外部接口是常见需求,当多个接口的请求参数、返回数据结构存在差异时,直接调用会导致代码逻辑冗余且难以维护。适配器模式可以将不兼容的接口转换为目标系统期望的接口,让原本无法一起工作的类或接口可以协同工作,下面结合实际场景讲解具体实现方法。

适配器模式的核心角色
Go语言中没有类的概念,通过结构体和接口来实现适配器模式,核心包含三个角色:
- 目标接口:系统期望使用的接口,定义了统一的调用规范
- 被适配者:需要被适配的原有接口或结构体,通常是已有的第三方服务、旧系统接口等
- 适配器:连接目标接口和被适配者的桥梁,实现目标接口,内部调用被适配者的方法完成功能
基础场景实现示例
假设我们有两个不同的支付接口,一个是旧版支付接口,一个是新版支付接口,现在需要统一用新版接口的规范调用两个支付服务。
1. 定义目标接口
首先定义系统期望的统一支付目标接口,所有支付操作都按照这个接口规范调用:
// 目标接口:统一支付接口
type PaymentTarget interface {
Pay(amount float64) string
}
2. 定义被适配者
旧版支付接口和新版支付接口的实现逻辑不同,分别定义为被适配者:
// 被适配者1:旧版支付接口
type OldPayment struct{}
func (o *OldPayment) OldPay(money int) string {
return fmt.Sprintf("旧版支付成功,金额:%d分", money)
}
// 被适配者2:第三方支付接口,参数和返回值格式不同
type ThirdPartyPayment struct{}
func (t *ThirdPartyPayment) SendPayment(total float64, currency string) string {
return fmt.Sprintf("第三方支付成功,金额:%.2f%s", total, currency)
}
3. 实现适配器
针对两个被适配者分别实现适配器,让它们都符合目标接口的规范:
// 旧版支付适配器
type OldPaymentAdapter struct {
oldPayment *OldPayment
}
func NewOldPaymentAdapter(old *OldPayment) PaymentTarget {
return &OldPaymentAdapter{oldPayment: old}
}
func (a *OldPaymentAdapter) Pay(amount float64) string {
// 将目标接口的float64金额转换为旧接口需要的int分
money := int(amount * 100)
return a.oldPayment.OldPay(money)
}
// 第三方支付适配器
type ThirdPartyPaymentAdapter struct {
thirdPayment *ThirdPartyPayment
}
func NewThirdPartyPaymentAdapter(third *ThirdPartyPayment) PaymentTarget {
return &ThirdPartyPaymentAdapter{thirdPayment: third}
}
func (a *ThirdPartyPaymentAdapter) Pay(amount float64) string {
// 适配第三方接口的参数格式,固定货币为人民币
return a.thirdPayment.SendPayment(amount, "元")
}
4. 调用验证
通过适配器统一调用两个支付接口,无需关心底层接口的差异:
package main
import "fmt"
func main() {
// 初始化被适配对象
oldPay := &OldPayment{}
thirdPay := &ThirdPartyPayment{}
// 创建适配器
oldAdapter := NewOldPaymentAdapter(oldPay)
thirdAdapter := NewThirdPartyPaymentAdapter(thirdPay)
// 统一用目标接口调用
var payment PaymentTarget
payment = oldAdapter
fmt.Println(payment.Pay(10.5)) // 输出:旧版支付成功,金额:1050分
payment = thirdAdapter
fmt.Println(payment.Pay(10.5)) // 输出:第三方支付成功,金额:10.50元
}
带额外逻辑的适配器实现
除了简单的参数转换,适配器还可以增加额外的业务逻辑,比如日志记录、参数校验等。下面的示例在适配第三方支付接口时增加支付金额校验逻辑:
type ValidatedThirdPartyAdapter struct {
thirdPayment *ThirdPartyPayment
}
func NewValidatedThirdPartyAdapter(third *ThirdPartyPayment) PaymentTarget {
return &ValidatedThirdPartyAdapter{thirdPayment: third}
}
func (a *ValidatedThirdPartyAdapter) Pay(amount float64) string {
if amount <= 0 {
return "支付金额必须大于0"
}
if amount > 10000 {
return "单笔支付金额不能超过10000元"
}
return a.thirdPayment.SendPayment(amount, "元")
}
适配器模式使用注意事项
- 适配器模式适合解决接口不兼容的问题,不要为了使用模式而强行适配,如果接口本身差异过大,可能需要考虑其他方案
- 适配器尽量只做接口转换和必要的参数适配,不要加入过多复杂的业务逻辑,避免适配器职责过重
- 如果被适配的接口后续会废弃,适配器可以作为过渡方案,待旧接口下线后可以直接移除适配器相关代码
| 适配场景 | 实现要点 |
|---|---|
| 参数格式转换 | 在适配器方法中完成目标接口参数到被适配者参数的类型、格式转换 |
| 返回值格式统一 | 将被适配者的返回值转换为目标接口定义的返回格式 |
| 多接口聚合适配 | 适配器可以同时调用多个被适配者的方法,组合完成目标接口的功能 |