作为一个Android程序员,RecyclerView、ListView是平时开发中经常会使用到的,可以说是非常亲切熟悉,而他们体现的设计模式正是适配器模式。
1 适配器模式概念
适配器模式(Adapter),将一个类的接口转换成客户希望的另外一个接口。Adapter模式使原本由于接口不兼容而不能一起工作的那些类一起工作。
当系统的数据和行为都正确,但是接口不符时,我们应该考虑使用适配器,目的是使控制范围之外的一个原有对象与某个接口匹配。适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况。
这里大致说一下适配器模式的使用场景:
- 系统需要使用现有的类,而此类的接口不符合系统的需要,即接口不兼容
- 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能将来引进的类一起工作
- 需要一个统一的输出接口,而输入端的类型不可预知
而RecyclerView就很明显是适用于上面的第一个场景:
RecyclerView
这个类需要在多个Activity、Fragment中展示不同的列表,但是我们客户端只有数据集,而RecyclerView怎么可能直接根据我们的数据集来展示数据呢。所以我们重写了 ReyclerView.Adapter<JavaBean>
,将数据进行了绑定,在任何场景,我们只需要把这个 Adapter传给RecyclerView,他就能展示我们想要的数据了。
RecyclerView.Adapter
连接了RecyclerView
和数据集
,将多种多样的数据集,全部转成可以被RecylerView所识别和展示的东西,这就体现出了适配器模式。
当然了,如果读过Retrofit源码,就会知道Retrofit.create()
源码中:
adapter()
会根据事先设置好的CallAdapter
来适配并返回不同的Call。
所以Retrofit的返回可以适配像RxJava、RxAndroid这样的情况。这里就不多做细讲,有兴趣的可以自己查看。
2. UML图
来看下适配器模式的UML图,它非常的简单,我们甚至在了解RecyclerView后就能自己画出来:
就是三大块,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. 总结
可以看到,在上面写翻译类的时候,它的工作其实是让真正别的类去做的,这有一点点像桥接模式,或者说,在这个场景下,它就是桥接模式。而且,翻译类又像是别的类真正实现,这又有一点点像“外观模式”。
可以看下这篇文章:设计模式学习笔记十四:适配器模式、桥接模式与外观模式就讲述的很清楚。
Adapter模式的经典实现在于将原本不兼容的接口融合在一起,使之能够很好地进行合作。它的优点:
- 更好的复用性
系统需要使用现有的类,而此类的接口不符合系统的需要,那么通过适配器模式就可以让这些功能得到更好的应用。 - 更好的扩展性
在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能
缺点:
- 过多的使用适配器,会让系统非常凌乱,不易整体把握。例如,明明看到调用的是A接口,其实内部被适配成了B接口的实现,一个系统如果出现太多这种情况,无异于异常灾难,因此,如果不是很有必要,可以不使用适配器,而是对系统进行重构。