原文首发于我的博客:https://kaolengmian7.com/post...

示例代码已经上传到我的 Github:https://github.com/kaolengmian7/golang-demo/tree/master/design_pattern/adapter,可以把代码拉下来跑一跑~

核心思想

适配器是两个不兼容的接口之间的桥梁,使接口不兼容的对象能够相互合作。

使用场景

有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。
1. 封装有缺陷的接口
2. 封装多个接口
3. 替换依赖
4. 兼容老版本接口
5. 适配不同格式数据

一定要注意:适配器为的是解决正在服役的项目的问题,而不是在设计阶段添加的。如果在设计阶段就考虑使用适配器模式,那这个设计一定不合格。

实战

假如你正在开发一款股票市场监测程序, 它会从不同来源下载 XML 格式的股票数据, 然后向用户呈现出美观的图表。

type XmlHandler interface {
 ProcessXml(xml string)}

type xmlHandler struct{}

func (x *xmlHandler) ProcessXml(xml string) {
 log.Printf("xml:%s processing", xml)}

在开发过程中, 你决定在程序中整合一个第三方智能分析函数库。 但是遇到了一个问题, 那就是分析函数库只兼容 JSON 格式的数据。

// 分析库接口
type JsonHandler interface {
 ProcessJson(json []byte)}

type jsonHandler struct {
}

func (j *jsonHandler) ProcessJson(json []byte) {
 log.Printf("json processing")}

image.png
你可以修改函数库来支持 XML。 但是, 这可能需要大量代码。 或者,我们可以将 XML 转换成 JSON?

xml 与 json 代表了现实中不兼容的两个接口。
为了演示方便,我用string类型表示xml文件,用[]byte表示json文件

适配器就是为两个兼容两个接口而生,在此例中:xmlAdapter继承了XmlHandler接口,其中封装了对接口的处理。结果就是,客户端的 JSON 处理被“适配”成 XML 处理。

type xmlAdapter struct {
 JsonHandler}

func (x *xmlAdapter) ProcessXml(xml string) {
 // 模拟对象转换:xml -> json
 json := []byte(xml) // 处理 json 文件
 x.ProcessJson(json)}

func TestAdapter(t *testing.T) {
 inputJson := []byte("json_file") // 原客户端
 handler := &jsonHandler{} handler.ProcessJson(inputJson)
 // 客户端接入适配器,利用 原有的 JsonHandler 处理 xml 文件。
 inputXml := "xml_file" adapter := &xmlAdapter{&jsonHandler{}} adapter.ProcessXml(inputXml)}

优点

灵活性强,上线新功能不需要改动大量源代码,仅需要在接口层做一层类型转换。

缺点

过多地使用适配器,会让系统非常零乱。比如,表明上看到调用的是 A 接口,其实内部被适配成了 B 接口的实现。
一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,尽量不使用适配器,而是直接对系统进行重构。

更进一步:加强版适配器

对于 Golang 这种可以实现多继承的语言来讲,适配器有更优雅的解决方案:让适配器实现所有接口。

type adapter struct {
 jsonHandler JsonHandler xmlHandler  XmlHandler}

这样的适配器强大的多,想用谁的实现就用谁的实现,与此同时完全规避了上面提到的缺点,不容易产生误解。

func TestAdapterInGolang(t *testing.T) {
 inputJson := []byte("json_file") inputXml := "xml_file" // 原客户端
 handler := &jsonHandler{} handler.ProcessJson(inputJson)
 // 客户端接入适配器
 adapter := &adapter{&jsonHandler{}, &xmlHandler{}} adapter.jsonHandler.ProcessJson(inputJson) // 想用 json 用 json adapter.xmlHandler.ProcessXml(inputXml)    // 想用 xml 用 xml}

也可以重写函数:

type adapter struct {}

func (x *adapter) ProcessJson(json []byte) {
 // 自定义处理逻辑
}

func (x *adapter) ProcessXml(xml string) {
 // 自定义处理逻辑
}

func TestAdapterInGolang(t *testing.T) {
 inputJson := []byte("json_file") inputXml := "xml_file" // 原客户端
 handler := &jsonHandler{} handler.ProcessJson(inputJson)
 // 客户端接入适配器
 adapter := &adapter{&jsonHandler{}, &xmlHandler{}} adapter.ProcessJson(inputJson) // 想用 json 用 json adapter.ProcessXml(inputXml)    // 想用 xml 用 xml}

参考:

  1. https://refactoringguru.cn/design-patterns/adapter
  2. http://shusheng007.top/2021/09/08/018/
  3. https://www.runoob.com/design-pattern/adapter-pattern.html
  4. https://lailin.xyz/post/adapter.html#comments

已注销
1 声望2 粉丝