适配器模式
适配器模式是一种设计模式,其将一个类的接口转换成另一个指定的接口,以实现原本由于接口不兼容而无法协同工作的类之间的协同工作。
通常,在完成某些任务时需要与其他程序进行通信,如访问数据库、访问开发接口、第三方授权登录或短信通知等。
代码中任何外部依赖性都会带来设计和测试方面的问题。此时,可以使用适配器模式,同时解决这两个问题。
简单地说,如果程序需要接入 MySQL、PostgreSQL、SQLite 等数据库,直接针对每种数据库编写 CURD 操作将导致设计难度加大,不便于测试。使用适配器模式,则只需了解增删改查操作,具体实现由包含依赖关系的组件负责实现。
将特定于依赖关系的内容封装在一个组件中,进行核心流程测试时,可替换为 mock 组件,模拟完成依赖对象的操作。
代码示例
场景
目前,该程序已接入微信支付功能,现欲加入 PayPal 支付功能。
1
2
3
4
5
6
|
// 微信支付
type WeChatPay struct{}
func (w *WeChatPay) Pay(money int64) {
fmt.Println("微信支付")
}
|
1
2
3
4
5
6
7
|
type Payer struct{
Pay(int64)
}
func main(){
var payer Payer = new(WeChatPay)
payer.Pay()
}
|
1
2
3
4
5
6
|
// 等待接入的 PayPal 支付
type PayPal struct{}
func (w *PayPal) Consume(money float64,message string){
fmt.Println("PayPal 支付")
}
|
不使用设计模式
将会在代码块里,直接依赖第三方库,没有解耦,难以测试,切代码块读起来复杂。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
func main(){
payType := "PayPal"
switch payType{
case "PayPal":
var payer Payer = new(WeChatPay)
payer.Pay(12)
case "Wechat":
pp := new(PayPal)
pp.Consume(12.0, "购买会员")
default:
fmt.Println("不支持的支付方式")
}
}
|
使用适配器模式
当 PayPal 的支付组件由别人提供时,不能修改代码。此时由我们定义一个适配器实现接口,内部实现调用 PayPal。
1
2
3
4
5
6
7
8
|
// 定义 PayPal 对 Payer 的适配器
type PayPalAdopter struct{
payPal *PayPal
}
func (p *PayPalAdopter) Pay(money int64) {
p.payPal.Consume(float64(money),"购买会员")
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
func main() {
payType := "PayPal"
var payer Payer
switch payType{
case "PayPal":
payer = new(WeChatPay)
case "Wechat":
payer = new(PayPalAdopter{ new(PayPal)})
default:
fmt.Println("不支持的支付方式")
}
payer.Pay(12)
}
|
通过以上的方式使测试更容易,因为它更容易推理,不必担心两个不同且不相关的行为会互相干扰。
其他结构型模式
- 适配器模式通常在已有程序中使用,让互相不兼容的类友好合作,先有两端的东西,才有适配器。桥接模式,通常开发前期进行设计,使各个部分独立开发便于开发,先有桥才有两端的东西。
参考
The adapter pattern in Go
适配器模式