设计模式--适配器模式的思考


个人认为适配器模式是一种加中间层来解决问题的思想,为的是减少开发工作量,提高代码复用率.另外在对于第三方的服务中使用适配器层则可以很好的把自己系统与第三方依赖解耦,降低依赖.

什么是适配器模式

适配器模式: 将一个类的接口转换为客户所期望的另一个接口.适配器让原本接口不兼容的类可以合作无间.类图如下:

Client: 调用方 Target: 需要提供的新功能 AdaptedObject: 系统中原本存在的类似本次需要提供的新功能的类 Adapter: Target的实现类,主要负责该功能的实现,其内部持有AdaptedObject的对象,利用其对象完成本次需要提供的新功能.

整个流程大概如下: 1.客户通过目标接口调用适配器的方法发出请求. 2.适配器(Adapter)使用被适配器(AdaptedObject)已有的功能完成客户所期望的新功能 3.客户收到调用结果,但是并不知道是适配器起到的转换作用. 那么Adapter利用已经完成的AdaptedObject类实现本次提供的新功能,这一过程就是适配.

Java I/O中的适配器

在Java I/O中有把字节流转换为字符流的类java.io.InputStreamReader以及java.io.OutputStreamWriter.那么这两个类实际上使用的就是适配器模式 以InputStreamReader为例,其继承了Reader类,所提供的功能是把字节流转换为字符流,其内部拥有StreamDecoder这一实例,所有的转换工作是由该实例完成.

public int read(char cbuf[], int offset, int length) throws IOException {
    // 使用被适配器的功能
    return sd.read(cbuf, offset, length);
}

那么在这个例子中 Client是调用方,也就是我们开发人员 Target是Reader这个抽象类. AdaptedObject是StreamDecoder,利用的是其功能. Adapter是InputStreamReader

Java Set集合中的适配器

Java中的Set集合有者无序,唯一元素,查找复杂度O(1)等特性.这些特性Map数据结构的key是完全符合的,那么就可以利用适配器模式来完成Set的功能. 以HashSet为例,其内部持有的是一个值为固定Object的Map,如下图

其所有的操作会通过HashSet这个适配器来操作HashMap这个被适配器.比如:

public Iterator<E> iterator() {
        return map.keySet().iterator();
}
 public boolean add(E e) {
        return map.put(e, PRESENT)==null;
}

Client是调用方,也就是我们开发人员 Target是Set这个接口. AdaptedObject是HashMap,利用的是其功能. Adapter是HashSet

Mybatis中的适配器模式

Mybatis作为一款通用框架,对于日志处理必然需要适配到各种日志框架,比如slf4j,log4j,logback等,每个日志的API多多少少有点不同,这种情况下适配器模式就起到了转换的作用. 以下图由于实现类太多,只列取了几个.

Mybatis有自己的org.apache.ibatis.logging.Log接口,框架内部使用的都是自己的Log,具体使用哪一个Log是由配置中的适配器决定的. 以org.apache.ibatis.logging.log4j2.Log4j2LoggerImpl适配器为例,org.apache.logging.log4j.Logger为被适配者.Log4j2LoggerImpl是适配器,起到了转换的作用.

public class Log4j2LoggerImpl implements Log {
  
  private static final Marker MARKER = MarkerManager.getMarker(LogFactory.MARKER);
  //被适配者
  private final org.apache.logging.log4j.Logger log;

  public Log4j2LoggerImpl(Logger logger) {
    log = logger;
  }

  @Override
  public boolean isDebugEnabled() {
    return log.isDebugEnabled();
  }
  .....
}

与装饰者模式的区别

个人认为这两种设计模式是完全不同的思想: 装饰者模式本意是增强功能,其装饰者与被装饰者对于调用方是很清晰的,比如ContreteDecoratorA decoratorA = new ContreteDecoratorA(new ComponentInterfaceImpl());就很清晰的知道使用ContreteDecoratorA装饰了ComponentInterfaceImpl.另外ContreteDecoratorA并没有改变ComponentInterfaceImpl的功能提供出去,而是为其进行了增强处理. 适配器模式本意是复用已有的代码,对已经存在的功能进行包装转换,以另一种形式提供出去.比如HashSet,对于调用方来说其内部使用的HashMap是不可见的,调用方不关心内部被适配者是谁,只是关注该功能本身也就是Set接口. 要说相同点的话那就是都是组合复用思想对一个对象进行包装,但其目的有着本质的区别.还望好好理解.

与外观模式的区别

外观模式本意是把一组复杂的关联行为进行包装,提供一个面向开发人员更为简单的使用方式.举个例子,你觉得JDBC方式不太好用,因此写了个DBUtils这种封装类,实际上就是一种外观模式,与适配器还是有着很大的区别.

备注

一家之言,个人主观理解还是有很多的,如果有错误还请指出.

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏技术点滴

适配器模式(Adapter)

适配器模式(Adapter) 适配器模式(Adapter)[Wrapper] 意图:将类的一个接口转换成用户希望的另一个接口,使得原本由于接口不兼容而不能一起工...

21190
来自专栏向治洪

系统捕获异常并发送到服务器

大家都知道,现在安装Android系统的手机版本和设备千差万别,在模拟器上运行良好的程序安装到某款手机上说不定就出现崩溃的现象,开发者个人不可能购买所有设备逐个...

21070
来自专栏流媒体

Android RTMP推流之MediaCodec硬编码一(H.264进行flv封装)

在前面Android平台下使用FFmpeg进行RTMP推流(摄像头推流)的文章中,介绍了如何使用FFmpeg进行H264编码和Rtmp推流。接下来讲分几篇文章来...

93530
来自专栏求索之路

Android数据层架构的实现 下篇

接上篇:Android数据层架构的实现 上篇 4.外观模式实现数据处理引擎框架暴露出来的api 我们在使用各种开源框架的时候,大多数时候都不会对框架内部...

36350
来自专栏Android开发指南

3.数据存储

31970
来自专栏Golang语言社区

GoLang并发控制(下)

context的字面意思是上下文,是一个比较抽象的词,字面上理解就是上下层的传递,上会把内容传递给下,在go中程序单位一般为goroutine,这里的上下文便是...

34130
来自专栏腾讯大讲堂的专栏

深入浅出 Retrofit,这么牛逼的框架你们还不来看看?

文章来源:腾讯Bugly Android 开发中,从原生的 HttpUrlConnection 到经典的 Apache 的 HttpClient,再到对前面这些...

33050
来自专栏林冠宏的技术文章

浅谈 Glide - BitmapPool 的存储时机 & 解答 ViewTarget 在同一View显示不同的图片时,总用同一个 Bitmap 引用的原因

作者:林冠宏 / 指尖下的幽灵 掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8 博客:htt...

425100
来自专栏Android开发指南

Android优化指南

53070
来自专栏飞雪无情的博客

Go语言实战笔记(二十)| Go Context

控制并发有两种经典的方式,一种是WaitGroup,另外一种就是Context,今天我就谈谈Context。

21430

扫码关注云+社区

领取腾讯云代金券