Golang设计模式(三):适配器模式

适配器模式

什么是适配器

适配器模式(Adapter Pattern):将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。

适配器模式的分类:

  1. 类适配器模式:通过多重继承机制,将Adaptee类适配到Target接口。
  2. 对象适配器模式:通过持有一个Adaptee对象的实例,将Adaptee类适配到Target接口。

基本流程

  1. 定义目标接口(Target)
    • 确定你的应用程序需要的接口(即目标接口),这个接口定义了应用程序期望的交互方式。
  2. 识别被适配者(Adaptee)
    • 找到一个已经存在的类或对象(被适配者),它实现了一些功能,但接口与目标接口不兼容。
  3. 创建适配器类(Adapter)
    • 创建一个适配器类,它实现目标接口,并在内部维护一个被适配者的实例。
  4. 实现转换逻辑
    • 在适配器类中,实现目标接口定义的方法,并通过内部的被适配者实例来执行相应的操作,实现接口的转换。
  5. 客户端调用
    • 客户端代码通过目标接口与适配器对象交互,适配器对象将请求转换为被适配者可以理解的格式,并调用被适配者的方法。
  6. 适配器对象的实例化
    • 在客户端代码中,创建适配器对象的实例,并将被适配者对象传递给适配器。
  7. 使用适配器
    • 客户端通过适配器对象来访问被适配者的功能,无需关心被适配者的接口细节。

结构

适配器模式的组成:

  1. Target(目标接口):定义客户端使用的特定领域相关的接口。
  2. Adaptee(被适配者):一个已经存在的类,需要适配。
  3. Adapter(适配器):通过在内部包装一个Adaptee对象,把源接口转换成目标接口。

优点

  • 兼容性:可以让不兼容的接口能够一起工作。
  • 解耦:适配器模式将目标接口与被适配者解耦,使得它们可以独立变化,只需保证适配器的实现正确即可。
  • 灵活性:增加了新的接口不需要修改原有代码,符合开闭原则。即对扩展开放,对修改封闭。

缺点

增加系统的复杂度:每增加一个需要适配的类,都需要增加一个适配器。

使用场景

  1. 接口不兼容:当需要使用一个类,但这个类的接口与当前系统的接口不兼容时。
  2. 第三方库:需要与第三方库集成,但第三方库的接口不符合系统要求时。
  3. 保持接口独立:希望在不修改现有接口的情况下,增加新的功能或行为。
  4. 对象组合:希望将一个或多个不兼容的对象组合到一个统一的接口下。

注意事项

  1. 适配器数量:考虑系统是否能够承受引入多个适配器带来的复杂性。
  2. 适配器职责:适配器应该保持职责单一,只关注于接口的转换。

代码案例

类适配器模式

类适配器模式使用继承来实现适配器。在 Go 语言中,由于不支持多重继承,我们通常不使用类适配器模式。

组合优于继承:Go 鼓励使用组合(composition)而不是继承(inheritance)来实现代码复用。

  • 继承:是一种静态的关系,子类在编译时就确定了其基类。继承是一种强耦合关系,子类依赖于基类的实现细节。
  • 组合:是一种动态的关系,一个对象可以在运行时动态地包含另一个对象。组合是一种弱耦合关系,对象之间的依赖仅仅是接口。

对象适配器模式

对象适配器模式通过组合来实现适配器,这是 Go 语言中更常见的适配器模式实现方式。

简单案例

// Target 接口
func Target interface{
	Request()
}
  
// Adaptee 接口
type Adaptee struct{}
func(a *Adaptee)SpecificRequest(){
	fmt.Println("Adaptee specific request")
}

// 对象适配器
type OjectAdapter struct{
	adaptee *Adaptee
}
func (o *OjectAdapter)Request(){

	o.adaptee.SpecificRequest()
}
func main() {
	adaptee := adapter.Adaptee{}
	// 实例化适配器,并传入被适配者对象
	objectAdapter := adapter.ObjectAdapter{&adaptee}
	objectAdapter.Request() // 使用对象适配器模式
}

复杂案例

// 美国插座接口
type USASocket interface {
    PlugIn()
}

// 英式插头
type BritishSocket struct{}

func (b *BritishSocket) PlugIn() {
    fmt.Println("Plug into British socket")
}

// 欧式插头
type EuropeanSocket struct{}

func (e *EuropeanSocket) Insert() {
    fmt.Println("Insert into European socket")
}
// 适配器
type SocketAdapter struct {
    europeanSocket *EuropeanSocket
}

func (sa *SocketAdapter) PlugIn() {
    sa.europeanSocket.Insert()
}
// 在使用美国插座时,无需区分插头类型,统一使用USASocket接口
func useUSASocket(socket USASocket) {
    socket.PlugIn()
}
// 客户端代码
func main() {
    britishSocket := &BritishSocket{}

    europeanSocket := &EuropeanSocket{}
    adapter := &SocketAdapter{
        europeanSocket: europeanSocket,
    }

    // 使用适配器将欧式插头适配成美式插座
    useUSASocket(britishSocket)
    useUSASocket(adapter)
}

我们定义了USASocket接口作为统一的充电器接口,BritishSocketEuropeanSocket分别代表英式和欧式插头接口。然后,我们创建了一个SocketAdapter适配器,将EuropeanSocket适配成了USASocket接口。在main函数中,我们先创建了一个英式插头对象和一个欧式插头对象,然后使用适配器将欧式插头适配成了统一的美式插座接口。

  • 28
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值