概念
适配器模式(Adapter Pattern)
将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。
适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
注:在适配器模式定义中所提及的接口是指广义的接口,它可以表示一个方法或者一组方法的集合。
Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。
Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对 Adaptee 和 Target 进行适配。适配器类是适配器模式的核心,在对象适配器模式中,它通过继承 Target 并关联一个 Adaptee 对象使二者产生联系。
Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配。适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。
应用
多云管理 CMP 需要纳管多种私有云平台,例如:OpenStack 和 VMware 。
通常一个成熟的系统都会提供调用其接口的多种开发语言 SDK。例如:OpenStack、VMware 都有这样的 SDK 提供。
这种官方提供的 SDK 就是 Adaptee,我们一般不修改 SDK 的源码,而是基于其做集成开发。
这种 SDK 定义的数据模型和我们自己开发的服务定义的数据模型肯定是不一样的,除非你直接使用它的数据模型。而我们的数据模型通常会基于我们的自己的应用层来设计。因此,转换就不可避免了,实际上我们在不知不觉中就使用了适配器模式。
下面是简单的代码示例:
adaptee_openstack_sdk.go
adaptee_vmware_sdk.go
virtual_machine_target.go
openstack_adapter.go
vmware_adapter.go
client.go
代码语言:javascript复制# adaptee_openstack_sdk.go
package main
import "fmt"
type OpenStackVirtualMachineSDK struct {
Name string
Flavor string
}
func (vm OpenStackVirtualMachineSDK) createVM() {
fmt.Println("I am OpenStack SDK.")
fmt.Println("Call OpenStack API to create VM.")
fmt.Println()
}
func (vm OpenStackVirtualMachineSDK) deleteVM() {
fmt.Println("I am OpenStack SDK.")
fmt.Println("Call OpenStack API to delete VM.")
fmt.Println()
}
代码语言:javascript复制# adaptee_vmware_sdk.go
package main
import "fmt"
type VMwareVirtualMachineSDK struct {
Name string
Flavor string
}
func (vm VMwareVirtualMachineSDK) createVM() {
fmt.Println("I am OpenStack SDK.")
fmt.Println("Call OpenStack API to create VM.")
fmt.Println()
}
func (vm VMwareVirtualMachineSDK) deleteVM() {
fmt.Println("I am OpenStack SDK.")
fmt.Println("Call OpenStack API to delete VM.")
fmt.Println()
}
代码语言:javascript复制# virtual_machine_target.go
package main
type VirtualMachineTarget interface {
create()
delete()
}
代码语言:javascript复制# openstack_adapter.go
package main
type OpenStackVirtualMachineAdapter struct {
Name string
Flavor string
}
func (vm OpenStackVirtualMachineAdapter) create() {
OpenStackVirtualMachineSDK{vm.Name, vm.Flavor}.createVM()
}
func (vm OpenStackVirtualMachineAdapter) delete() {
OpenStackVirtualMachineSDK{vm.Name, ""}.deleteVM()
}
代码语言:javascript复制# vmware_adapter.go
package main
type VMwareVirtualMachineAdapter struct {
Name string
Flavor string
}
func (vm VMwareVirtualMachineAdapter) create() {
VMwareVirtualMachineSDK{vm.Name, vm.Flavor}.createVM()
}
func (vm VMwareVirtualMachineAdapter) delete() {
VMwareVirtualMachineSDK{vm.Name, ""}.deleteVM()
}
代码语言:javascript复制# client.go
package main
func main() {
var virtualMachineType = "OpenStack"
var vm VirtualMachineTarget
if virtualMachineType == "OpenStack" {
vm = OpenStackVirtualMachineAdapter{Name: "An OpenStack VirtualMachine", Flavor: "1C2G"}
vm.create()
vm.delete()
} else if virtualMachineType == "VMware" {
vm = VMwareVirtualMachineAdapter{Name: "An VMware VirtualMachine", Flavor: "2C4G"}
vm.create()
vm.delete()
}
}
总结
适配器模式中有一个 “变化” 和一个 “不变”。
1、“不变” 的是被适配者提供的核心功能是不变,正因为需要使用其核心功能,才需要适配它。
2、“变化” 的是被适配者的交互 “界面” 被改变了,这个界面的改变就是适配器的核心工作。
因此,适配器中应该是数据模型的转换,而不应该有大量的业务逻辑,业务逻辑需要给更上一层的 Service 去实现。
开发过程中的命名最好也按照设计模式中概念的名字来,这样也算是 DDD 中通用语言的践行。