适配器模式(Adapter Pattern)是经典的设计模式之一,属于结构型模式。在 Go 语言中,适配器模式通过接口(interface)和组合(composition)可以非常优雅地实现。
一、什么是适配器模式?
🎯 核心思想:
“让不兼容的接口能够一起工作”
就像现实中的电源适配器:
中国插座是 220V 两孔,而日本电器是 110V 三孔,你需要一个“适配器”来让电器在中国使用。
✅ 应用场景:
- 调用第三方库,但其接口与你的代码不匹配
- 复用旧代码,但接口已过时
- 集成不同系统的组件
二、Go 中适配器模式的实现方式
Go 没有“继承”,但通过 接口 + 组合 可以轻松实现适配器。
🔧 三要素:
- 目标接口(Target):你期望的接口
- 被适配者(Adaptee):已有的、但接口不匹配的类/对象
- 适配器(Adapter):包装被适配者,实现目标接口
三、实战示例:日志系统适配
假设你有一个系统,使用 Logger
接口记录日志:
// 目标接口
type Logger interface {
Log(message string)
}
但你引入了一个第三方库,它的日志接口是这样的:
// 被适配者(Adaptee)
type ThirdPartyLogger struct{}
func (t *ThirdPartyLogger) Write(msg string, level int) {
// 第三方日志实现
fmt.Printf("[Level %d] %s\n", level, msg)
}
它的 Write(msg, level)
和你的 Log(message)
不兼容。
✅ 解决方案:创建适配器
package main
import "fmt"
// 1. 目标接口
type Logger interface {
Log(message string)
}
// 2. 被适配者
type ThirdPartyLogger struct{}
func (t *ThirdPartyLogger) Write(msg string, level int) {
fmt.Printf("[Level %d] %s\n", level, msg)
}
// 3. 适配器
type LoggerAdapter struct {
thirdParty *ThirdPartyLogger
}
// 实现目标接口 Log
func (a *LoggerAdapter) Log(message string) {
// 将 Log 调用适配为 Write 调用
a.thirdParty.Write(message, 1) // 假设 1 是 INFO 级别
}
// 使用示例
func main() {
// 创建第三方日志器
thirdPartyLogger := &ThirdPartyLogger{}
// 通过适配器包装
logger := &LoggerAdapter{thirdParty: thirdPartyLogger}
// 现在可以统一使用 Log 接口
logger.Log("用户登录成功")
logger.Log("文件上传完成")
}
✅ 输出:
[Level 1] 用户登录成功
[Level 1] 文件上传完成
四、另一种形式:双向适配器(可选)
有时你还需要反向适配,比如旧系统调用新接口。
// 假设新接口更丰富
type AdvancedLogger interface {
Info(msg string)
Error(msg string)
}
type AdvancedLoggerAdapter struct {
simple Logger
}
func (a *AdvancedLoggerAdapter) Info(msg string) {
a.simple.Log("[INFO] " + msg)
}
func (a *AdvancedLoggerAdapter) Error(msg string) {
a.simple.Log("[ERROR] " + msg)
}
五、适配器模式 vs 其他模式
模式 | 区别 |
---|---|
装饰器模式 | 增强功能,而不是转换接口 |
代理模式 | 控制访问,通常接口相同 |
外观模式 | 简化复杂系统接口,而不是转换 |
✅ 适配器 = 接口转换
装饰器 = 功能增强
代理 = 访问控制
六、Go 中适配器模式的优势
优势 | 说明 |
---|---|
✅ 接口解耦 | 你的代码依赖 Logger 接口,不依赖具体实现 |
✅ 复用现有代码 | 无需修改第三方库代码 |
✅ 符合开闭原则 | 对扩展开放,对修改关闭 |
✅ 易于测试 | 可用 mock 适配器进行单元测试 |
七、实际应用场景
集成不同支付网关
- 支付宝:
PayAli(amount, orderID)
- 微信:
WXPay(order)
- 适配为统一的
PaymentGateway.Pay(amount)
- 支付宝:
数据库驱动适配
- MySQL 驱动接口 vs PostgreSQL 驱动接口 → 适配为统一的
DBInterface
- MySQL 驱动接口 vs PostgreSQL 驱动接口 → 适配为统一的
API 版本兼容
- V1 API 与 V2 API 不兼容 → 用适配器统一调用方式
八、注意事项
- 不要过度使用:如果接口差异太大,可能需要重新设计,而不是强行适配。
- 性能开销:适配器是一层包装,有轻微调用开销(通常可忽略)。
- 避免“适配器链”:不要出现
A → B → C → D
的层层适配,会增加复杂度。
✅ 总结
问题 | 回答 |
---|---|
适配器模式是什么? | 让不兼容的接口能一起工作 |
Go 中如何实现? | 接口 + 组合,包装被适配者 |
核心价值 | 解耦、复用、兼容 |
适用场景 | 集成第三方库、旧系统升级、多系统整合 |
🔑 一句话:
适配器模式 = “翻译官”,它把“你说的话”翻译成“别人能听懂的语言”。
掌握它,你就能写出更灵活、可扩展的 Go 程序!