将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。适配器模式分为类适配器模式和对象适配器模式。前者类之间的耦合度比后者高,并且要求程序员了解现有组件库中相关组件的内部结构。所以应用的相对少点。
结构:
目标接口(Target):当前系统业务所期望的接口,它可以是抽象类或者接口。【客户提供】
适配者类(Adaptee):它是被访问和适配的现存组件库中的组件接口。【系统现有】
适配器类(Adapter):它是一个转换器,通过继承或者引用适配者对象,把适配者接口转换成目标接口,让客户按照目标接口的格式访问适配者。【转接口】
类适配器模式:
定义一个适配器类来实现当前业务的接口,同时又继承现有组件库中已存在的组件。
【例】现有一台电脑只能读取SD卡,而现在需要读取一张TF卡中的内容。相比于换电脑,转接口更符合实际。所以在这个模式下用到了适配器模式。创建一个读卡器类,将TF卡中内容读取出来。
/**
* 适配器模式 类适配器模式
* 适配者接口 【定义格式】
* TF卡
*/
public interface TFCard {
/**
* 从TF卡中读取数据
* @return TF卡中现存数据
*/
String readTF();
/**
* 往TF卡写入数据
* @param msg 待写入
*/
void writeTF(String msg);
}
/**
* 适配器模式 类适配器模式
* 适配者类
* 【我们需要将卡中数据在电脑上被读取和写入。所以这里是适配者
电脑提供的接口是目标接口,也是客户期望的接口】
* 具体的那张 TF卡
*/
public class TFCardImpl implements TFCard{
@Override
public String readTF() {
String msg = "TF卡 读取信息: hello world";
return msg;
}
@Override
public void writeTF(String msg) {
System.out.println("TF卡 写入信息:" + msg);
}
}
/**
* 适配器模式 类适配器模式
* 目标接口 【定义格式】
* SD卡
*/
public interface SDCard {
/**
* 从SD卡中读取数据
* @return TF卡中现存数据
*/
String readSD();
/**
* 往SD卡写入数据
* @param msg 待写入
*/
void writeSD(String msg);
}
/**
* 适配器模式 类适配器模式
* 目标接口类
* 【这里是电脑本身的接口,也就是客户本身提供的接口,所以这里对于程序员来说是目标接口类】
* 具体的那张 SD卡
*/
public class SDCardImpl implements SDCard {
@Override
public String readSD() {
String msg = "SD卡 读取信息: hello world";
return msg;
}
@Override
public void writeSD(String msg) {
System.out.println("SD卡 写入信息:" + msg);
}
}
/**
* 适配器模式 类适配器模式
* 客户端类
* 只能读取SD卡信息的电脑
*/
public class Computer {
/**
* 读取SD卡
* @param sdCard 需要一张SD卡
* @return sd卡的内容
*/
public String readSD(SDCard sdCard){
if (sdCard == null){
throw new NullPointerException("SD卡不能为空");
}
return sdCard.readSD();
}
}
/**
* 适配器模式 类适配器模式
* 适配器类
* 转接口 SD - TF 适配器
*/
public class Adapter extends TFCardImpl implements SDCard {
@Override
public String readSD() {
System.out.println("适配器生效 ---> 读取TF卡中的数据");
return readTF();
}
@Override
public void writeSD(String msg) {
System.out.println("适配器生效 ---> 往TF卡中写入数据" + msg);
writeTF(msg);
}
}
类适配器模式违背了合成复用原则。假如有多个适配者类,java并不支持多继承。
对象适配器模式:
定义一个适配器类来实现当前业务的接口,同时又引入现有组件库中已存在的组件。
这样不仅修复了类适配器模式的合成复用原则的缺陷,也可以不强制客户端提供接口。
其他功能不变,只需要改动适配器即可
/**
* 适配器模式 对象适配器模式
* 适配器类
* 转接口 SD - TF 适配器
*/
public class AdapterNew implements SDCard {
//聚合TFCard 接口而非继承
private TFCard tfCard;
//声明有参构造函数获取实际的 tfCard
public AdapterNew(TFCard tfCard){
this.tfCard = tfCard;
}
@Override
public String readSD() {
System.out.println("适配器生效 ---> 读取TF卡中的数据");
return tfCard.readTF();
}
@Override
public void writeSD(String msg) {
System.out.println("适配器生效 ---> 往TF卡中写入数据" + msg);
tfCard.writeTF(msg);
}
}
使用场景:
以前开发的老系统存在满足新新系统功能需求的类,但是其接口不一致
使用第三方组件库,但是对方提供的接口和我们自己拟定的接口定义不同
JDK源码分析:
Reader【字符输入流】、InputStream【字节输入流】的适配使用的是 InputStreamReader
InputStreamReader 继承自java.io包中的reader,对它的未实现抽象方法给出了具体实现。
可以将字节流转换为字符流。
其中的sd 【StreamDecoder对象,解码对象。将字节流转成字符流】,Sun在JDK的实现中,实际是对 sun.nio.cs.StreamDecoder对象的引用
在上面的关系网中使用了适配器模式,StreamDecoder是Sun公司的具体实现,它是一个标准的适配器。它实现了字符流对字节流的转换,实现是同一个read接口,但是做了不一样的数据返回。