适配器模式

如果代码不可测试,那么它不是一个好的设计。

适配器模式

适配器模式是一种设计模式,其将一个类的接口转换成另一个指定的接口,以实现原本由于接口不兼容而无法协同工作的类之间的协同工作。

通常,在完成某些任务时需要与其他程序进行通信,如访问数据库、访问开发接口、第三方授权登录或短信通知等。

代码中任何外部依赖性都会带来设计和测试方面的问题。此时,可以使用适配器模式,同时解决这两个问题。

简单地说,如果程序需要接入 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

适配器模式

本文阅读量 次, 总访问量 ,总访客数
Built with Hugo .   Theme Stack designed by Jimmy