适配器模式

参考书籍

go design patterns for real world project

介绍

旧的接口过期了,但替换旧的接口需要成本(如时间)。作为替代,创建一个新的接口,利用hood,去使用旧的接口
生活中插头的转换便是一个适配器。再例如type-c孔到USB孔的转接头,转接头就是一个适配器
优点
- 具备可预测性。在我们的apps上地维护基于 open/closed 原则1,使得apps的行为更加容易预测
- 免修改。使用别人的功能,但不用对其进行修改

目标

两个不匹配的部分需要一起工作时可以考虑使用适配器模式(or facade pattern,for example)。如两个不匹配的接口需要一起工作,可以考虑适配器模式

在适配器对象上使用不兼容的接口

在下面的例子中,有一个旧的Printer接口和一个新的Printer接口。新的接口和旧的接口有不一样的函数签名(参数)。通过使用一个适配器,使用户仍旧能够使用旧的实现(要与一些遗留的代码一起工作)

要求和验收标准

我们有:

旧的接口新的接口
LegacyPrinterModernPrinter

创建一个结构体实现:ModernPrinter接口并能且会使用(调用)LegacyPrinter接口。步骤如下:

  1. 创建一个适配器对象,实现ModernPrinter接口
  2. 这个新的适配器对象必须包含LegacyPrinter接口的实例
  3. 必须调用LegacyPrinter,并加上前缀字符串 Adapter.

单元测试我们的 Printer adapter

我们首先写下旧(legacy。后面我们会混合使用旧或者遗留,意识是一样的)代码,我们不会测试它,并想象这并不是我们的代码。

type LegacyPrinter interface {
	Print(s string) string
}

type MyLegacyPrinter struct{}

func (l *MyLegacyPrinter) Print(s string) string {
	newMsg := fmt.Sprintf("Legacy Printer:%s\n", s) //修改文本,添加前缀Legacy Printer:
	println(newMsg)//输出结果
	return newMsg// 返回修改后的字符
}

遗留的接口被称作LegacyPrinter并且有一个Print方法。MyLegacyPrint结构体实现了LegacyPrinter接口。

现在声明我们要改变的新的接口

type ModernPrinter interface {
	PrintStored() string
}

在这个例子中,新的PrintStored方法并不会接收字符串作为参数,而是存储在实例中。我们会调用适配器模式的PrinterAdapter接口

type PrinterAdapter struct {
	OldPrinter LegacyPrinter
	Msg        string
}

func (p *PrinterAdapter) PrintStored() (newMsg string) {
	//实现内容会在后面展示
	return
}

像之前提到的,PrinterAdapter适配器必须拥有字段用于存储待打印的字符。它还必须拥有一个字段来存储LegacyPrinter适配器的实例。所以我们的单元测试如下:

func TestAdapter(t *testing.T) {
	//我们将使用Hello World!作为适配器的消息
	msg := "Hello World!" 
	//我们创建了一个PrinterAdapter的实例叫做adapter
	//传递了MyLegacyPrinter结构体的实例,作为LegacyPrinter字段,称为OldPrinter
	//我们还传递了我们需要的消息(Msg)
	adapter := PrinterAdapter{OldPrinter: &MyLegacyPrinter{}, Msg: msg}
	//接着,使用ModernPrinter 接口的PrintStored方法,该方法并不需要参数,改方法会返回修改后的字符
	//我们知道,MyLegacyPrinter结会体返回传递的字符加上前缀LegacyPrinter:,适配器会添加前缀Adapter:,
	returnedMsg := adapter.PrintStored()
	//所以,最终我们的文本需要满足:"Legacy Printer:Adapter:Hello World!\n"
	if returnedMsg != "Legacy Printer:Adapter:Hello World!\n" {
		t.Errorf("Message didn't match:%s\n", returnedMsg)
	}
	
	//我们正在传递一个接口的实例,我们需要考虑指针为空的情况。所以我们增加了下面的测试
	adapter = PrinterAdapter{OldPrinter: nil, Msg: msg}
	returnedMsg = adapter.PrintStored()
	if returnedMsg != "Hello World!" {
		t.Errorf("Message didn't match:%s", returnedMsg)
	}
}

实现Implementation


type PrinterAdapter struct {
	OldPrinter LegacyPrinter
	Msg        string
}

func (p *PrinterAdapter) PrintStored() (newMsg string) {
	//检查是否有LegacyPrinter实例
	if p.OldPrinter != nil {
		//添加Adapter前缀
		newMsg = fmt.Sprintf("Adapter:%s", p.Msg)
		//通过LegacyPrinter输出结果
		newMsg = p.OldPrinter.Print(newMsg)
	} else {
		//如果没有实例直接返回存储的字符
		return p.Msg
	}
	return
}

执行测试
在这里插入图片描述

最后所有的代码放在一起(adapter_test)

// file name:adapter_test
package main_test

import (
	"fmt"
	"testing"
)

type LegacyPrinter interface {
	Print(s string) string
}

type MyLegacyPrinter struct{}

func (l *MyLegacyPrinter) Print(s string) string {
	newMsg := fmt.Sprintf("Legacy Printer:%s\n", s)
	println(newMsg)
	return newMsg
}

type ModernPrinter interface {
	PrintStored() string
}

type PrinterAdapter struct {
	OldPrinter LegacyPrinter
	Msg        string
}

func (p *PrinterAdapter) PrintStored() (newMsg string) {
	if p.OldPrinter != nil {
		newMsg = fmt.Sprintf("Adapter:%s", p.Msg)
		newMsg = p.OldPrinter.Print(newMsg)
	} else {
		return p.Msg
	}
	return
}

func TestAdapter(t *testing.T) {
	msg := "Hello World!"
	adapter := PrinterAdapter{OldPrinter: &MyLegacyPrinter{}, Msg: msg}
	returnedMsg := adapter.PrintStored()
	if returnedMsg != "Legacy Printer:Adapter:Hello World!\n" {
		t.Errorf("Message didn't match:%s\n", returnedMsg)
	}

	//处理空指针
	adapter = PrinterAdapter{OldPrinter: nil, Msg: msg}
	returnedMsg = adapter.PrintStored()
	if returnedMsg != "Hello World!" {
		t.Errorf("Message didn't match:%s", returnedMsg)
	}
}

执行代码

go test -v .

执行结果
在这里插入图片描述
在这里插入图片描述


  1. 什么是open/closed:
    最初由Bertrand Meyer在Object-Oriented Software Construction一书中提出
    代码应该对新功能开放,对修改关闭。1:代码应该具备可扩展性,而不是修改。避免修改源代码,是因为我们并不总是能够意识到对代码修改的潜在影响。2:请记住,只有通过使用设计模式和面向接口的编程,才可能实现代码的可扩展性 ↩︎

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值