适配器模式简介
适配器模式主要解决在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的问题。
适配器模式(Adapter Pattern) 是作为两个不兼容的接口之间的桥梁,它是结构型设计模式 的一种。适配器模式将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
适配器模式让原本两个不兼容的类能够兼容,并且从用户的角度看不到具体的转换,用户只是调用适配器完成转换,在用户看来,直接和目标接口交互就可以了。
实现方式
适配器模式包含三种角色:
- 目标接口(Target):当前系统业务所期待的接口,可以是抽象类或接口。
- 适配者(Adaptee):被访问和适配的现存组件库中的组件接口。
- 适配器(Adapter):适配器模式的核心角色,其他两个角色都是已经存在的角色,而适配器角色是重新建立的角色。它是一个转换器,通过继承或者引用适配器对象,把适配者接口转换为目标接口,让客户能够通过目标接口访问适配者。
适配器模式包括三类:类适配器模式、对象适配器模式、接口适配器模式
以一个日常生活中的简单例子来说明:
假如我们家里有一个电器需要一个两孔的插座,但是家里只有一个三孔插座,这个时候我们就需要一个适配器,把两孔的插头插到三孔的插座上面。
1.类适配器模式
类适配器模式通过继承适配者接口,实现目标接口,实现了在适配器类中获取了两个接口与中的方法,从而实现方法对象的转换,但是由于java的单继承机制,这就要求目标必须是接口,具有一定的局限性。
- 耦合性高
- 灵活性低
- 实现简单
实现代码如下:
//目标接口
interface Target{
void request(); //三孔插座
}
//适配者接口
class Adaptee{
public void specificRequest(){
System.out.println("插头插入了两孔插座"); //需要一个两孔插座
}
}
//适配器类
class ClassAdapter extends Adaptee implements Target{
@Override
public void request() {
specificRequest(); //将三孔插座转换为两孔
}
}
//客户端 测试类
public class AdapterTest {
public static void main(String[] args) {
Target target=new ClassAdapter();
target.request(); //用户使用了三孔的插座,但是插入的是两孔
}
}
输出:
可以看到在客户端我们调用了目标接口,也就是说我们使用的是三孔的插座,因为在适配器中实现了将三孔插座转换为两孔插座,最终实现了两孔的插头使用了三孔的插座,就好像是使用的两孔插座一样。
2. 对象适配器模式
对象适配模式中的适配器类不再继承适配者的类,而是通过组合的方式在适配器类中获取适配者对象,从而实现将适配者转换为目标类。对比来说,比类适配器更加灵活。
- 耦合性低
- 灵活性高
- 实现较复杂
//适配器类
class ClassAdapter implements Target{
private Adaptee adaptee;
public ClassAdapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest(); //将三孔插座转换为两孔
}
}
//客户端 测试类
public class AdapterTest {
public static void main(String[] args) {
Target target=new ClassAdapter(new Adaptee());
target.request(); //用户使用了三孔的插座,但是插入的是两孔
}
}
3. 接口适配器模式
接口适配器模式借助中间抽象类空实现目标接口中的所有方法,子类选择性重写,可以减少不必要的方法。
从表面上看,就好像是不再是客户端使用适配器,而是适配者自己去使用适配器为客户端提供我们需要的用途。
实现代码:
//目标接口
interface Target{
void request(); //三孔插座
}
//适配者接口
class Adaptee extends ClassAdapter{
@Override
public void request(){
System.out.println("插头插入了两孔插座"); //需要一个两孔插座
}
}
//适配器类
/**
* 抽象类:提供缺省实现
*/
class ClassAdapter implements Target{
@Override
public void request() {
}
}
//客户端 测试类
public class AdapterTest {
public static void main(String[] args) {
Adaptee adaptee=new Adaptee();
adaptee.request(); //用户使用了三孔的插座,但是插入的是两孔
}
}
总结
优点:
- 客户端通过适配器可以透明地调用目标接口。 复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类。
- 将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题。 在很多业务场景中符合开闭原则。
缺点:
- 由于java特性的限制,最多只能适配一个适配者类,并且目标类必须是抽象类或接口。
- 增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。比如,明明看到的是A接口,其实内部被适配成了B接口的实现,如果一个系统中这种情况过多,整个系统会非常凌乱。因此,如果不是必要,尽量少使用适配器。
应用场景:有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。