原文首发于我的博客: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")}
你可以修改函数库来支持 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}
参考:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。