1,适配器模式(Adapter,包装器模式,Wrapper)
1)定义
将一个类的接口,转换成客户期待的另一个接口。适配器类让原本不兼容的类合作无间。属于结构型模式。
2)参与者
① Adaptee(适配者类)
需要被适配的类、接口、对象,这些是已经存在的形式;
② Target (目标抽象类)
最终需要的形式
③ Adapter(适配器)
转换器,对Adaptee和Target进行适配。适配者类的三种形式分别对应三种适配器模式,类适配器、接口适配器、对象适配器。
3)场景
- 系统需要使用现有的类,而此类的接口不符合系统的需要。
- 要建立一个可以重用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。
(这些源类不一定有一致的接口。) - 需要一个统一的输出接口,而输入端的类型不可预知。
(比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口。)
注意事项:适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。即组合模式的使用是在设计、开发完成后,发现现存类和复用环境不一致的问题才使用的。
4)举例
①手机电源适配器
②ADO.NET的DataAdapter,是用作DataSet和数据源之间的适配器。DataAdapter通过映射Fill和Update来提供这一适配器。
5)Android中的适配器模式
ListView的Adapter、GridView的Adapter、RecyclerView的Adapter都使用了适配器模式。
对于ListView,GridView,RecyclerView的Adapter来说,它们三个控件需要的是View(Target ),而我们有的一般是datas(src),所以适配器Adapter就是完成了数据源datas 转化成 ItemView的工作。
2,类的适配器模式
1)概念
Adapter类,通过继承 Adaptee类,实现Target 类接口,完成 Adaptee->Target 的适配。
2)类图(继承)
3)demo
充电器的例子。
i> Adaptee类,有220V电压:
public class Voltage220 {
public int output220V() {
int src = 220;
System.out.println("我是" + src + "V");
return src;
}
}
ii> 我们需要的Target 接口:输出5V。
public interface Voltage5 {
int output5V();
}
iii> 适配器类:将220V(Adaptee)转换为5V(Target )。
public class VoltageAdapter extends Voltage220 implements Voltage5 {
@Override
public int output5V() {
int src = output220V();
System.out.println("适配器工作开始适配电压");
int dst = src / 44;
System.out.println("适配完成后输出电压:" + dst);
return dst;
}
}
iv>使用
Voltage5 source = new ClassAdapter();
source.output5V();
4)优缺点
有局限性:
- Target 必须为接口才可以使用。(java是单一继承)
- 只能适配一个适配者
3,对象的适配器模式(推荐)
1)概念
持有Adaptee类的实例,以解决兼容性的问题。
即:持有 Adaptee类,实现 Target 类接口,完成 Adaptee->Target 的适配。
2)类图(组合)
3)demo
还是充电器例子:
这种场景需要适配多个适配者对象,类适配器不能满足。此时用对象适配器来实现。
//适配者2
public class VoltageAmerican {
public void outputAmerican(){
System.out.println("美式插头转换成国标插头成功");
}
}
/**
* 对象的适配器模式 通过关联来实现 持有adaptee类的实例
*/
public class ObjectAdapter implements Voltage5 {
public int output5V() {
Voltage220 mVoltage220 = new Voltage220();
VoltageAmerican voltageAmerican = new VoltageAmerican();
int dst = 0;
if (null != mVoltage220) {
voltageAmerican.outputAmerican();
int src = mVoltage220.output220V();
System.out.println("对象适配器工作:开始适配电压");
dst = src / 44;
System.out.println("适配完成后输出电压:" + dst);
}
return dst;
}
}
这和我们常用的Android中的Adapter相同。
4)优缺点
根据合成复用原则,组合大于继承。
所以它解决了类适配器必须继承Adaptee的局限性问题,也不再强求Target 必须是接口。
4,接口的适配器模式(默认适配器模式,Default Adapter Pattern,缺省适配器模式)
1)概念
当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求,它适用于一个接口不想使用其所有的方法的情况。
2)类图
类似于模板方法,新的Adapter类中由两种方法组成:
抽象方法:由子类强制实现;
钩子方法:Adapter中定义为空方法,子类选择实现。
3)举例
①插板
我们有一个插板,上面有多个插座。多个插板放办公室,但我们只用来插主机,其他的插座不用不安全,我们不想要这些除三孔之外的插座。
/**
* 电源插座接口
*/
public interface SocketInterface {
public void HoleTwo();
public void HoleThree();
public void USB();
/**
* 美式插座
*/
public void American();
}
给办公室放很多插板,每个插板都要实现4个方法,但我们只要一个三孔插座的方法,不想每次都多实现另外3个方法。
public class Socket implements SocketInterface {
public void HoleTwo() {
}
public void HoleThree() {
}
public void USB() {
}
public void American() {
}
}
然后我们定义一个接口适配器,把我们需要的方法设置为抽象方法,其他方法设置为空方法。
public abstract class InterfaceAdapter implements SocketInterface {
public void HoleTwo() {
}
public abstract void HoleThree();
public void USB() {
}
public void American() {
}
}
新的插板诞生了:
public class SocketThree extends InterfaceAdapter{
public void HoleThree() {
}
}
②AnimatorListenerAdapter类,就是一个接口适配器
属性动画ValueAnimator类,通过addListener(AnimatorListener listener)方法添加监听器,常规写法:
ValueAnimator valueAnimator = ValueAnimator.ofInt(0,100);
valueAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
valueAnimator.start();
如果不想实现Animator.AnimatorListener接口的全部方法,只想监听onAnimationStart,则如下写法:
ValueAnimator valueAnimator = ValueAnimator.ofInt(0,100);
valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
//xxxx具体实现
}
});
valueAnimator.start();
源码如下:
Adapter类:
public abstract class AnimatorListenerAdapter implements Animator.AnimatorListener,
Animator.AnimatorPauseListener {
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationPause(Animator animation) {
}
@Override
public void onAnimationResume(Animator animation) {
}
}
对应的Adaptee:
public static interface AnimatorListener {
void onAnimationStart(Animator animation);
void onAnimationEnd(Animator animation);
void onAnimationCancel(Animator animation);
void onAnimationRepeat(Animator animation);
}
②WindowListener适配器
java.awt.event下的接口WindowListener源码:
public interface WindowListener extends EventListener {
public void windowOpened(WindowEvent e);
public void windowClosing(WindowEvent e);
public void windowClosed(WindowEvent e);
public void windowIconified(WindowEvent e);
public void windowDeiconified(WindowEvent e);
public void windowActivated(WindowEvent e);
public void windowDeactivated(WindowEvent e);
}
使用的时候需要实现每个方法:
WindowListener windowListener = new WindowListener(){
@Override
public void windowOpened(WindowEvent e) {
}
@Override
public void windowClosing(WindowEvent e) {
}
@Override
public void windowClosed(WindowEvent e) {
}
@Override
public void windowIconified(WindowEvent e) {
}
@Override
public void windowDeiconified(WindowEvent e) {
}
@Override
public void windowActivated(WindowEvent e) {
}
@Override
public void windowDeactivated(WindowEvent e) {
}
};
在某一种场景下我们只需要重写windowClosed方法,并且频繁实现这个接口,那么写一个Adapter类:
Adapter实现接口WindowListener,并将windowClosed方法设置为抽象类。
public abstract class WindowListenerAdapter implements WindowListener {
@Override
public void windowOpened(WindowEvent e) {
}
@Override
public void windowClosing(WindowEvent e) {
}
public abstract void windowClosed(WindowEvent e);
@Override
public void windowIconified(WindowEvent e) {
}
@Override
public void windowDeiconified(WindowEvent e) {
}
@Override
public void windowActivated(WindowEvent e) {
}
@Override
public void windowDeactivated(WindowEvent e) {
}
}
其它地方需要实现的时候直接实现Adapter抽象类即可:
WindowListenerAdapter windowListenerAdapter = new WindowListenerAdapter(){
@Override
public void windowClosed(WindowEvent e) {
}
};
5,三种适配器模式对比
1)实现
类适配器,以类给到,在Adapter里,就是将Adaptee当做类,继承,
对象适配器,以对象给到,在Adapter里,将Adaptee作为一个对象,持有。
接口适配器,以接口给到,在Adapter里,将Adaptee作为一个接口,实现。
6,优缺点
1)优
① 将目标类和适配者类解耦;
② 增加了类的透明性和复用性;
③ 可以让任何两个没有关联的类一起运行,一个对象适配器可以把多个不同的适配者适配到同一个目标。
2)缺
- 过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
- 对于类适配器,破坏了类的透明性,只能适配一个适配者类,而且目标类必须是抽象类;
- 对于对象适配器,修改起来比较复杂。
7,对比
1)适配器模式、装饰模式、外观模式对比
①意图
装饰者模式:不改变接口,但加入责任
适配器模式:将一个接口转成另一个接口
外观模式:让接口更简单