适配器介绍
Adapter:将一个类的接口转化成客户希望的另外的一个接口,乍一听十分弱智,你直接调用希望的那一个接口不就完了??
Adapter模式使原来接口不兼容,不能一起工作的类可以一起工作。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。
实现:类适配器和对象适配器
- 使用继承(就是所谓的类适配器模式)
- 使用组合(就是所谓的对象适配器模式)
适配器所涉及的角色有:
- Target(目标接口):所要转换的所期待的接口(笔记本电脑USB插口)
- Adaptee(源角色):需要适配的类(内存卡)
- Adapter(适配器):将源角色适配成目标接口,一般持有源接口的引用(或者继承源接口),且实现目标接口(读卡器)
- Client(客户类):通过目标角色获取服务(笔记本电脑)
优点: 1、可以让任何两个没有关联的类一起运行。 2、提高了类的复用。 3、增加了类的透明度。 4、灵活性好。
缺点: 1、过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。 2、由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。
示例代码
类适配器
电子产品的接口有TypeC插口和USB插口,我们现在将USB接口适配成TypeC插口,让只有TypeC插口的电子产品也可以使用USB插口
Adaptee:USB插口
/**
* Adaptee
* 存在一个USB插口
* 该插口已经实现其特定的USB插口的功能
*/
public class USB {
void isUSB() {
System.out.println("USB插口...");
}
}
Target:TypeC插口
/**
* Target
* TpyeC接口
* 即需要的目标接口
*/
public interface TypeC {
public void isTypeC();
}
Adapter:类适配器要继承正在使用的功能的类,并且实现想要转换的功能的接口
/**
* Adapter
* 类适配器
* 核心类:将USB适配成TypeC
*/
public class ClassAdapter extends USB implements TypeC {
@Override
public void isTypeC() {
// TypeC接口的实现是通过适配USB来实现的
super.isUSB();
}
}
Client
public class Client {
public static void main(String[] args) {
// 类适配器的实现
TypeC typeC = new ClassAdapter();
// 通过typeC提供服务
typeC.isTypeC();
}
}
运行结果:可以看出:调用了isTypeC()
方法打印的是USB的功能,实现了转换
对象适配器
我们有了上面的例子,只需要修改Adapter和Client即可,其余部分可保持不变。
Adapter:只需实现接口,实例是定义在类中的来调用之前的方法
public class ObjectAdapter implements TypeC {
// 保存需要适配的类对象
private USB usb;
// 通过构造函数实例化需要适配的类的对象
public ObjectAdapter(USB usb) {
this.usb = usb;
}
@Override
public void isTypeC() {
usb.isUSB();
}
}
Client:
public class Client {
public static void main(String[] args) {
TypeC typeC = new ObjectAdapter(new USB());
typeC.isTypeC();
}
}
运行结果:可以看出:调用了isTypeC()
方法打印的是USB的功能,实现了转换
IO中的使用
适配器在IO中的使用:
- 适配器角色就是
InputStreamReader
- 被适配的角色就是
InputStream
类的实例对象 - 目标接口是
Reader
类
目标类Reader操作字符:
public abstract class Reader implements Readable, Closeable
public int read(char[] cbuf)
需要适配的类InputStream操作字节:
public abstract class InputStream implements Closeable
public int read(byte[] b)
适配器类转换流InputStreamReader,InputStream是操作字节的
public class InputStreamReader extends Reader {
private final StreamDecoder sd;
public InputStreamReader(InputStream in) {
super(in);
try {
this.sd = StreamDecoder.forInputStreamReader(in, this, (String)null);
} catch (UnsupportedEncodingException var3) {
throw new Error(var3);
}
}
构造函数传递字节流,StreamDecoder
是一个解码类,将传进来的in字节解码成字符。
public int read(char[] cbuf, int offset, int length) throws IOException {
return this.sd.read(cbuf, offset, length);
}
InputStreamReader的read
操作内部调用的是sd的read
操作,这个方法是操作字节的。
这就是一个典型的适配器使用的一个典范,IO中类似的适配器还有很多,处理流中有很多例子。
适配器这种设计模式主要就是各种角色的划分,要划分好角色之后进行编码实现。
装饰器与适配器异同点
- 装饰器与适配器都有一个别名叫做 包装模式(Wrapper),它们看似都是起到包装一个类或对象的作用,但是使用它们的目的不相同
- 适配器模式:是要将一个接口转变成另外一个接口,它的目的是通过改变接口来达到重复使用的目的
- 装饰器模式:不是要改变被装饰对象的接口,而是恰恰要保持原有的接口,但是增强原有对象的功能
- 或者改变原有对象的处理方式而提升性能。所以这两个模式设计的目的是不同的
器与适配器都有一个别名叫做 包装模式(Wrapper),它们看似都是起到包装一个类或对象的作用,但是使用它们的目的不相同 - 适配器模式:是要将一个接口转变成另外一个接口,它的目的是通过改变接口来达到重复使用的目的
- 装饰器模式:不是要改变被装饰对象的接口,而是恰恰要保持原有的接口,但是增强原有对象的功能
- 或者改变原有对象的处理方式而提升性能。所以这两个模式设计的目的是不同的