天天看点

浅学设计模式之适配器模式(12/23)

作为一个Android程序员,RecyclerView、ListView是平时开发中经常会使用到的,可以说是非常亲切熟悉,而他们体现的设计模式正是适配器模式。

1 适配器模式概念

适配器模式(Adapter),将一个类的接口转换成客户希望的另外一个接口。Adapter模式使原本由于接口不兼容而不能一起工作的那些类一起工作。

当系统的数据和行为都正确,但是接口不符时,我们应该考虑使用适配器,目的是使控制范围之外的一个原有对象与某个接口匹配。适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况。

这里大致说一下适配器模式的使用场景:

  1. 系统需要使用现有的类,而此类的接口不符合系统的需要,即接口不兼容
  2. 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能将来引进的类一起工作
  3. 需要一个统一的输出接口,而输入端的类型不可预知

而RecyclerView就很明显是适用于上面的第一个场景:

​​

​RecyclerView​

​​这个类需要在多个Activity、Fragment中展示不同的列表,但是我们客户端只有数据集,而RecyclerView怎么可能直接根据我们的数据集来展示数据呢。所以我们重写了 ​

​ReyclerView.Adapter<JavaBean>​

​,将数据进行了绑定,在任何场景,我们只需要把这个 Adapter传给RecyclerView,他就能展示我们想要的数据了。

​RecyclerView.Adapter​

​​连接了​

​RecyclerView​

​​和​

​数据集​

​,将多种多样的数据集,全部转成可以被RecylerView所识别和展示的东西,这就体现出了适配器模式。

当然了,如果读过Retrofit源码,就会知道​

​Retrofit.create()​

​源码中:

浅学设计模式之适配器模式(12/23)

​adapter()​

​​会根据事先设置好的​

​CallAdapter​

​来适配并返回不同的Call。

所以Retrofit的返回可以适配像RxJava、RxAndroid这样的情况。这里就不多做细讲,有兴趣的可以自己查看。

2. UML图

来看下适配器模式的UML图,它非常的简单,我们甚至在了解RecyclerView后就能自己画出来:

浅学设计模式之适配器模式(12/23)

就是三大块,Target、Adapter、Adaptee。

在RecyclerView中,RecyclerView就是Target,RecyclerView.Adapter就是Adapter,数据集就是Adaptee。

3. 代码示例

适配器模式又叫翻译器模式,因为Adapter的作用就和“翻译”差不多,这很适合一些需要翻译的场景。

这里是《大话》中的例子,现在的场景是NBA,姚主席刚来到NBA,还不会说英语,所以他需要一个翻译,在没有翻译的时候,情况是这样的:

//球员抽象类,定义了进攻和防守:
public abstract class Player {
    protected String name;

    public Player(String name) {
        this.name = name;
    }

    public abstract void offense();

    public abstract void defense();
}

// 具体球员类,分为前锋、中锋和后卫:
public class Forwards extends Player {
    public Forwards(String name) {
        super(name);
    }

    @Override
    public void offense() {
        System.out.println("前锋" + name + "进攻");
    }

    @Override
    public void defense() {
        System.out.println("前锋" + name + "防守");
    }
}

....//中锋和后卫代码类似,这里省去

//客户端代码:
public class AdapterMain {
    public static void main(String[] args) {
        Player lbj = new Forwards("Lebron james");
        lbj.offense();
        Player kb = new Guards("Kobe Bryant");
        kb.offense();
    }
}      

这个时候,由于姚主席他并不适合Center,因为他并不知道attack和defense的意思,对于姚主席来说,他是一名外籍球员:

//外籍球员:
public class ForeignCenter {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    // 对中国人来说,只知道进攻,不知道offense
    public void jingong() {
        System.out.println("外籍中锋" + name + "进攻");
    }

    // 对中国人来说,只知道防守,不知道defense
    public void fangshou() {
        System.out.println("外籍中锋" + name + "防守");
    }
}      

这个时候就需要一个翻译类来帮助姚主席:

//翻译类
public class Translator extends Player {
    private ForeignCenter wjzf = new ForeignCenter();

    public Translator(String name) {
        super(name);
        wjzf.setName(name);
    }

    @Override
    public void offense() {
        wjzf.jingong();
    }

    @Override
    public void defense() {
        wjzf.fangshou();
    }
}      

翻译类就是Adapter,它继承了Player类,能调用offense,defense,在这些方法去调用外籍中锋类的方法。

这样,虽然使用的是 Translator,但是使用的是 ForeignCenter的方法。

// 客户端代码:
    public static void main(String[] args) {
        Player lbj = new Forwards("Lebron James");
        lbj.offense();
        Player kb = new Guards("Kobe Bryant");
        kb.offense();

        Player ym = new Translator("Yao Ming");
        ym.offense();
        ym.defense();
    }      

4. 总结

可以看到,在上面写翻译类的时候,它的工作其实是让真正别的类去做的,这有一点点像桥接模式,或者说,在这个场景下,它就是桥接模式。而且,翻译类又像是别的类真正实现,这又有一点点像“外观模式”。

可以看下这篇文章:​​​设计模式学习笔记十四:适配器模式、桥接模式与外观模式​​就讲述的很清楚。

  1. 更好的复用性

    系统需要使用现有的类,而此类的接口不符合系统的需要,那么通过适配器模式就可以让这些功能得到更好的应用。

  2. 更好的扩展性

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

  1. 过多的使用适配器,会让系统非常凌乱,不易整体把握。例如,明明看到调用的是A接口,其实内部被适配成了B接口的实现,一个系统如果出现太多这种情况,无异于异常灾难,因此,如果不是很有必要,可以不使用适配器,而是对系统进行重构。