一天一个设计模式:适配器模式

概念:

适配器模式是把一个类的接口变成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。

用途:

就像插头转换器,之前入了switch港版,插头是英式的,还好附赠一个插头转换器,适配器就相当于这个转换器。

种类:

分为类的适配器与对象的适配器两种

类适配器:

把适配的类的api转化为目标类的api

上图中Adaptee没有2方法,但是客户端却期望调用2方法。为了客户端能够使用Adaptee类,提供中间类Adapter,把Adaptee与Target的api组合起来,Adapter与Adaptee是继承关系,所以这决定了适配器是类适配器。

涉及到的角色:

  目标(Target)角色:这就是所期待得到的接口。注意:由于这里讨论的是类适配器,因此目标不能是类。

  源(Adapee)角色:现在需要适配的接口。

  适配器(Adapter)角色:适配器类是本模式的核心。适配器把源接口转化成目标接口,显然这一角色不可以是接口必须是具体类。

类适配器代码:

public interface Target {
    /**
     * 这是源类Adaptee也有的方法
     */
    public void sampleOperation1(); 
    /**
     * 这是源类Adapteee没有的方法
     */
    public void sampleOperation2(); 
}
public class Adaptee {
    
    public void sampleOperation1(){}

}
public class Adapter extends Adaptee implements Target {
    /**
     * 由于源类Adaptee没有方法sampleOperation2()
     * 因此适配器补充上这个方法
     */
    @Override
    public void sampleOperation2() {
        //写相关的代码
    }

}

适配器Adapter扩展了源类,也实现了目标接口,且提供了源类没有的接口的实现。


对象适配器模式

  对象适配器与类适配器不同的地方在于,对象适配器采用委派的方式将源类与适配器关联到一起(类适配器采用继承)

源代码:

public interface Target {
    /**
     * 这是源类Adaptee也有的方法
     */
    public void sampleOperation1(); 
    /**
     * 这是源类Adapteee没有的方法
     */
    public void sampleOperation2(); 
}
public class Adaptee {

    public void sampleOperation1(){}
    
}
public class Adapter {
    private Adaptee adaptee;
    
    public Adapter(Adaptee adaptee){
        this.adaptee = adaptee;
    }
    /**
     * 源类Adaptee有方法sampleOperation1
     * 因此适配器类直接委派即可
     */
    public void sampleOperation1(){
        this.adaptee.sampleOperation1();
    }
    /**
     * 源类Adaptee没有方法sampleOperation2
     * 因此由适配器类需要补充此方法
     */
    public void sampleOperation2(){
        //写相关的代码
    }
}

类适配器与对象适配器的权衡

  • 类适配器使用对象继承的方式,是静态的定义方式;而对象适配器使用的是对象组合的方式,是动态组合的方式。
  • 对于类适配器,由于适配器直接继承了Adaptee,使得适配器不能和Adaptee的子类一起工作,因为继承是静态的,当适配器继承Adaptee后,就不能再处理,Adaptee的子类。
  • 对于对象适配器,一个适配器可以把不同的源类适配到同一个目标中,换一种说法,同一个适配器可以把源类和它的子类都适配到目标接口。因为对象适配器采用的是对象组合的关系,只要类型正确,是不是子类都无所谓。
  • 对于类适配器,适配器可以重定义部分Adaptee的部分行为,相当于子类覆盖父类的方法。
  • 对于对象适配器,要重新定义源类的的行为很困难,这种情况下,需要定义Adaptee的子类来实现重定义,然后让适配器重新适配,虽然重定义Adaptee比较困难,但是想要增加一些新的行为则很方便,而且新增的行为可以适配所有的源。
  • 对于类适配器,仅仅引入了一个对象,并不需要额外的引用来间接得到Adaptee。
  • 对于对象适配器,需要额外的引用来简介得到Adaptee。

综上,尽量以对象适配器的实现方式为主,多用聚合,少用继承。当然具体问题具体分析,根据实际需要选择(个人认为实际上就没必要对源类进行重写,如果需要进行重写直接对源类进行修改就行了,除非是没有代码权限,不过如果没有权限的话,那么源类对你来讲更不可见,也就更没有重写的必要了吧。)。

适配器的优点:

  更好的复用性:

    系统需要使用现有的类,而此类的接口不符合系统的需要,那么通过适配器模式可以让类获得更好的复用。

  更好的扩展性:

    在适配器实现功能的时候,也可以调用自己的功能,从而扩展系统的功能。

适配器的缺点:

  过多的适配器,会让系统显得更加凌乱,不容易把控。

  重构的代价合适的话,确实要比适配器更合适。

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏移动开发的那些事儿

内存泄露的一些坑

如上,在Activity内部如果声明一个这样的Handler,那么myHandler就默认持有Activity引用,假设Activity退出了,但是可能这时候才...

28620
来自专栏difcareer的技术笔记

彻底弄懂dalvik字节码【三】0x01:0x02:0x03:0x04:0x05:

【一】、【二】中从代码的角度分析了dalvik字节码解释执行的过程,这篇文章以一个例子来实际分析一下。

16220
来自专栏非著名程序员

Retrofit OKHttp 教你怎么持久化管理Cookie

? 投稿作者:黄海杰 原文链接: http://blog.csdn.net/lyhhj/article/details/51345386 绪论 最近小编有点...

535100
来自专栏向治洪

[置顶] 浅谈我为什么选择用Retrofit作为我的网络请求框架

比较AsyncTask、Volley、Retrofit三者的请求时间 使用 单次请求 7个请求 25个请求 AsyncTask 94...

23550
来自专栏向治洪

Android的DataBinding原理介绍

Activity在inflate layout时,通过DataBindingUtil来生成绑定,从代码看,是遍历contentView得到View数组对象,然后...

49180
来自专栏Android开发与分享

【Android】Retrofit2.0源码解析

39190
来自专栏lzj_learn_note

阿里ARouter使用及源码解析(一)

在app的开发中,页面之间的相互跳转是最基本常用的功能。在Android中的跳转一般通过显式intent和隐式intent两种方式实现的,而Android的原生...

19520
来自专栏非著名程序员

Android开发工具类之ImageUtils

开发最重要的就是速度和效率,其实我一直都非常支持使用第三方的工具类,因为毕竟是一些大牛封装好的,效率什么的,可能比一些初学者写的确实好一些,但是我建议在使用第三...

21850
来自专栏Golang语言社区

一日一学_Go语言Context(设计及分析)

Go服务器的每个请求都有自己的goroutine,而有的请求为了提高性能,会经常启动额外的goroutine处理请求,当该请求被取消或超时,该请求上的所有gor...

43170
来自专栏分享达人秀

使用SimpleAdapter

通过ArrayAdapter实现Adapter虽然简单、易用,但ArrayAdapter的功能比较有限,它的每个列表项只能给一个TextView动态填充...

210100

扫码关注云+社区

领取腾讯云代金券