【Go 实现】实践 GoF 的 23 种设计模式:适配器模式
简单的分布式应用系统(示例代码工程):https://github.com/ruanrunxue/Practice-Design-Pattern--Go-Implementation
简介
适配器模式(Adapter)是最常用的结构型模式之一,在现实生活中,适配器模式也是处处可见,比如电源插头转换器,它可以让英式的插头工作在中式的插座上。
GoF 对它的定义如下:
Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.
简单来说,就是适配器模式让原本因为接口不匹配而无法一起工作的两个类/结构体能够一起工作。
适配器模式所做的就是将一个接口 Adaptee
,通过适配器 Adapter
转换成 Client 所期望的另一个接口 Target
来使用,实现原理也很简单,就是 Adapter
通过实现 Target
接口,并在对应的方法中调用 Adaptee
的接口实现。
UML 结构
场景上下文
在 简单的分布式应用系统(示例代码工程)中,db 模块用来存储服务注册信息和系统监控数据,它是一个 key-value 数据库。在 访问者模式 中,我们为它实现了 Table 的按列查询功能;同时,我们也为它实现了简单的 SQL 查询功能(将会在 解释器模式 中介绍),查询的结果是 SqlResult
结构体,它提供一个 toMap
方法将结果转换成 map
。
为了方便用户使用,我们将实现在终端控制台上提供人机交互的能力,如下所示,用户输入 SQL 语句,后台返回查询结果:
终端控制台的具体实现为 Console
,为了提供可扩展的查询结果显示样式,我们设计了 ConsoleRender
接口,但因 SqlResult
并未实现该接口,所以 Console
无法直接渲染 SqlResult
的查询结果。
为此,我们需要实现一个适配器,让 Console
能够通过适配器将 SqlResult
的查询结果渲染出来。示例中,我们设计了适配器 TableRender
,它实现了 ConsoleRender
接口,并以表格的形式渲染出查询结果,如前文所示。
代码实现
客户端这么使用:
在已经有了 Target 接口(ConsoleRender
)和 Adaptee(SqlResult
)的前提下,总结实现适配器模式的几个关键点:
定义 Adapter 结构体/类,这里是
TableRender
结构体。在 Adapter 中聚合 Adaptee,这里是把
SqlResult
作为TableRender
的成员变量。Adapter 实现 Target 接口,这里是
TableRender
实现了ConsoleRender
接口。在 Target 接口实现中,调用 Adaptee 的原有方法实现具体的业务逻辑,这里是在
TableRender.Render()
调用SqlResult.ToMap()
方法,得到查询结果,然后再对结果进行渲染。在 Client 需要 Target 接口的地方,传入适配器 Adapter 实例,其中创建 Adapter 实例时传入 Adaptee 实例。这里是在
NewTableRender()
创建TableRender
实例时,传入SqlResult
作为入参,随后将TableRender
实例传入Console.Output()
方法。
扩展
适配器模式在 Gin 中的运用
Gin 是一个高性能的 Web 框架,它的常见用法如下:
在实际运用场景中,可能存在这种情况。用户起初的 Web 框架使用了 Go 原生的 net/http
,使用场景如下:
因性能问题,当前客户准备切换至 Gin 框架,显然,myHttpHandler
因接口不兼容,不能直接注册到 gin.Default()
上。为了方便用户,Gin 框架提供了一个适配器 gin.WrapH
,可以将 http.Handler
类型转换成 gin.HandlerFunc
类型,它的定义如下:
使用方法如下:
在这个例子中,gin.Engine
就是 Client,gin.HandlerFunc
是 Target 接口,http.Handler
是 Adaptee,gin.WrapH
是 Adapter。这是一个 Go 风格的适配器模式实现,以更为简洁的 func
替代了 struct
。
典型应用场景
将一个接口 A 转换成用户希望的另外一个接口 B,这样就能使原来不兼容的接口 A 和接口 B 相互协作。
老系统的重构。在不改变原有接口的情况下,让老接口适配到新的接口。
优缺点
优点
能够使 Adaptee 和 Target 之间解耦。通过引入新的 Adapter 来适配 Target,Adaptee 无须修改,符合开闭原则。
灵活性好,能够很方便地通过不同的适配器来适配不同的接口。
缺点
增加代码复杂度。适配器模式需要新增适配器,如果滥用会导致系统的代码复杂度增大。
与其他模式的关联
适配器模式 和 装饰者模式、代理模式 在 UML 结构上具有一定的相似性。但适配器模式改变原有对象的接口,但不改变原有功能;而装饰者模式和代理模式则在不改变接口的情况下,增强原有对象的功能。
文章配图
可以在 用Keynote画出手绘风格的配图 中找到文章的绘图方法。
参考
[1] 【Go实现】实践GoF的23种设计模式:SOLID原则, 元闰子
[2] Design Patterns, Chapter 4. Structural Patterns, GoF
[3] 适配器模式, refactoringguru.cn
[4] Gin Web Framework, Gin
更多文章请关注微信公众号:元闰子的邀请
版权声明: 本文为 InfoQ 作者【元闰子】的原创文章。
原文链接:【http://xie.infoq.cn/article/fe4cc39a4fcf694dac815b3ed】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论