适配器模式
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。
这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。
介绍
意图:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
主要解决:主要解决在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的。
何时使用:
- 系统需要使用现有的类,而此类的接口不符合系统的需要。
- 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口。
- 通过接口转换,将一个类插入另一个类系中。(比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口。)
如何解决:继承或依赖(推荐)。
关键代码:适配器继承或依赖已有的对象,实现想要的目标接口。
应用实例:
- 美国电器 110V,中国 220V,就要有一个适配器将 110V 转化为 220V。
- JAVA JDK 1.1 提供了 Enumeration 接口,而在 1.2 中提供了 Iterator 接口,想要使用 1.2 的 JDK,则要将以前系统的 Enumeration 接口转化为 Iterator 接口,这时就需要适配器模式。
- 在 LINUX 上运行 WINDOWS 程序。
- JAVA 中的 jdbc。
优点:
- 可以让任何两个没有关联的类一起运行。
- 提高了类的复用。
- 增加了类的透明度。
- 灵活性好。
缺点:
- 过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了B接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
- 由于JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。
使用场景:有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。
注意事项:适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。
适配器模式涉及3个角色:
- 源(Adaptee):需要被适配的对象或类型。
- 适配器(Adapter):连接目标和源的中间对象。
- 目标(Target):期待得到的目标。
适配器模式包括3种形式:
- 类适配器模式
- 对象适配器模式
- 接口适配器模式(或又称作缺省适配器模式)
实现
先说说类适配器模式和对象适配器模式,我的第一把手机是港版的iphone5s,充电器是三只脚的,而内地又没有这样的插座,所以我需要额外再买一个转换器,把三脚转化为两脚的。此处,充电头也就称作源,转换器称作适配器,目标也就是转变成两脚的插头。
类适配器模式
/**
* 两脚插头,即目标
*/
public interface Two {
void twoCharge();
}
/**
* 三角的插头,源
*/
public class Three {
public void threeCharge() {
System.out.println("三角插头接收到电流~");
System.out.println("手机开始充电~");
}
}
/**
* 适配器
*/
public class AdapterClass extends Three implements Two {
@Override
public void twoCharge() {
System.out.println("两脚插头接收到电流~");
System.out.println("插头转换~~");
super.threeCharge();
}
}
public class Main {
public static void main(String[] args) {
AdapterClass adapter = new AdapterClass();
adapter.twoCharge();
}
}
输出结果:
两脚插头接收到电流~
插头转换~~
三角插头接收到电流~
手机开始充电~
主要方法就是将适配器继承源,并实现目标接口,利用目标接口的方法调用源的方法,适配器可在调用源的方法之前做一些转换的行为,然后再调用源的方法,实现转换。
对象适配器模式
public class AdapterObject implements Two {
private Three three;
public AdapterObject(Three three) {
this.three = three;
}
@Override
public void twoCharge() {
System.out.println("两脚插头接收到电流~");
System.out.println("插头转换~~");
three.threeCharge();
}
}
主要方法就是将适配器继承目标接口,并让源作为适配器的成员,在适配器中实现目标接口的方法即可。
接口适配器模式(或又称作缺省适配器模式)
此时我们有一个按钮,该按钮有一个监听接口
public class Button {
private ButtonListener listener;
public void setListener(ButtonListener listener) {
this.listener = listener;
}
}
public interface ButtonListener {
// 按下按钮的回调
void buttonPress();
// 松开按钮的回调
void buttonRelease();
}
按照以往的方法,我们需要这么实现
Button button = new Button();
button.setListener(new ButtonListener() {
@Override
public void buttonPress() {
// do nothing
}
@Override
public void buttonRelease() {
System.out.println("按钮已经按下,开始登录!");
}
});
从注释中看出,即使我们不使用buttonPress()回调,但是还是要重写他,此时我们构造一个接口适配器,其中接口空实现
public abstract class ButtonAdapter implements ButtonListener {
@Override
public void buttonPress() {
}
@Override
public void buttonRelease() {
}
}
只需要这么设置ButtonListener
Button btn = new Button();
btn.setListener(new ButtonAdapter() {
@Override
public void buttonRelease() {
System.out.println("按钮已经按下,开始登录!");
}
});
此时,我们就可以从实际需求中考虑需要回调哪一个回调,仅仅重写我们需要的方法即可。
适配器模式的优缺点
优点
- 更好的复用性:系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。
- 更好的扩展性:在实现适配器功能的时候,可以扩展自己源的行为(增加方法),从而自然地扩展系统的功能。
缺点
会导致系统紊乱:滥用适配器,会让系统变得非常零乱。例如,明明看到调用的是A接口,其实内部被适配成了B接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。