1.前言
其它的设计模式可能经常用但却不知道名字,但这篇文章所讲的是那种,哪怕不知道原理也肯定听过名字的模式。适配器对于安卓开发人员来说,是个耳熟能详的组件。只要使用列表控件,必然会用到它来连接视图和数据。大家都知道这两者分属View层和Model层,通过接口与外部保持通信。若类型差距甚远的它们想要交互:
- 第一种方法就是改变各自的接口。先不说View层基本由SDK提供API而不是源码,就算Model层支持修改,但当初定义接口是为了通用性,怎么可能一个应用修改一次。
- 第二种方法便是提供一个中间类,分别与两个接口对接,起到转换器的作用。就像不同齿距的齿轮,中间再加一个齿轮,就能协同工作了。
2.概念
适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。通过学习《Android源码设计模式解析与实战》,知道以下两种情况可以使用:
- 系统需使用现有的类,而此类的接口不符合要求,即接口不兼容;
- 想要建立一个可以重复使用的类(统一的输出接口),用来与没太大关联且可能不断引进的类(输入端的类型不可预知)一起工作,典型是列表的适配器。
3.场景
有一个人想喝酒,他种了满园的葡萄。虽然收获到了葡萄但是自己不会酿酒,仍然喝不到酒。后来附近开了酒厂,他把葡萄送过去加工,换来了等价的红酒。其中,酒厂就相当于是适配器。
4.写法
// 目标或输出接口
public interface Man {
void drink();
}
// 提供源接口或输入端类型
public class Vineyard {
public String provide() {
return "葡萄";
}
}
// 适配器提供接口转换
public class Winery extends Vineyard implements Man {
@Override
public void drink() {
switch (provide()) {
case "葡萄":
System.out.println("我喝到了红酒");
break;
default:
System.out.println("我啥也没喝到");
}
}
}
public class Main {
public static void main(String[] args) {
// 通过新建的酒厂喝到酒
new Winery().drink();
}
}
上面的代码展示的是类适配器模式,即适配器类通过继承源接口的实现类和实现目标接口,来获取输入和输出,并在内部完成转换。由于Java不支持多继承,所以目标接口不能用类替换。
通过前面的学习,知道可以用代理模式替换继承关系,即通过持有的对象来实现自己的行为。在保证逻辑的情况下,还能增加结构的灵活性,同时避免了多继承,允许目标接口用类替换,这便是对象适配器模式。
public class Winery implements Man {
// 持有源接口的对象
private Vineyard vineyard;
public Winery(Vineyard vineyard) {
this.vineyard = vineyard;
}
@Override
public void drink() {
switch (vineyard.provide()) {
case "葡萄":
System.out.println("我喝到了红酒");
break;
default:
System.out.println("我啥也没喝到");
}
}
}
public class Main {
public static void main(String[] args) {
// 通过新建的酒厂喝到酒
new Winery(new Vineyard()).drink();
}
}
这么改后,还发现了另一个好处。源接口中的方法不会暴露出来,即相比类适配器模式,适配器中少了继承源接口后引入的多余方法,降低用户学习成本。
5.总结
在使用适配器模式时,本着多用合成或聚合,少用继承的原则。对于列表控件的适配器,即使子项View各种各样,它只要知道返回的是View类型即可,让用户来处理解析数据、绑定展示控件等操作。达到隔离变化、拥抱变化的目的,也提高了代码的复用性和扩展性。