1、概述
- 什么是适配器模式?
适配器模式是一种结构型设计模式。适配器模式就是:把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
用电器来打个比喻:有一个电器的插头是三脚的,而现有的插座是两孔的,要使插头插上插座,我们需要一个插头转换器,这个转换器即是适配器。 - 适配器模式涉及3个角色:
- 源(Adaptee):需要被适配的对象或类型,相当于插头。
- 适配器(Adapter):连接目标和源的中间对象,相当于插头转换器。
- 目标(Target):期待得到的目标,相当于插座。
2、适配器模式UML图
- 通过上面UML图可以知道,客户端期待的接口或者对象通过适配器的转换得到了满足,Adapter通过内部包装Adaptee对象把源对象转换成客户端期待的对象。
3、适配器模式分类
适配器模式包括3种形式:
- 类适配器模式:类适配器使用的是继承的方式,一般来说无法对其子类进行适配
- 对象适配器模式:对象适配器使用的是组合的方式,子孙类都可以被适配。另外,对象适配器对于增加一些新行为非常方便,而且新增加的行为同时适用于所有的源。
- 接口适配器模式(又称缺省适配器模式):接口适配器模式(缺省适配模式)基本思想是,为一个接口提供缺省实现,这样子类可以从这个缺省实现进行扩展,而不必从原有接口进行扩展。
- 可以说对象适配器模式是另外6种结构型设计模式的起源
4、三种适配器模式详解
- 适配器模式的三种实现方式及代码实例详解,用电器来打个比喻:有一个电器的插头是两脚的,而现有的插座是三孔的,要使插头插上插座,我们需要一个插头转换器,这个转换器即是适配器。
4.1、类适配器模式
- 类适配器使用的是继承的方式,一般来说无法对其子类进行适配,请看代码实例
- 首先我们有一个要被适配的类
/**
* 源(相当于两脚插头,也就是被适配的类)
* @author ningbeibei
*/
public class Adaptee {
public void adapteeMethod() {
System.out.println("两脚插头,被适配的类....");
}
}
- 定义一个目标接口
/**
* 目标(客户所期待的类,可以是一个接口抽象类具体的类)
* 相当于三孔插板
* @author ningbeibei
*/
public interface Target {
void targetMethod();
}
- 定义适配器类
- 定义适配器类通过继承 Adaptee 和实现 Target 接口关联起来
/**
* 类适配器模式(相当于转换器)
* 通过Adapter类把Adaptee类与Target接口衔接起来
* @author ningbeibei
*/
public class Adapter extends Adaptee implements Target {
@Override
public void targetMethod() {
//操作处理
adapteeMethod();
//操作处理
}
}
- 测试代码
/**
* 适配器模式测试类
* @author ningbeibei
*/
public class test {
public static void main(String[] args) {
//类适配器模式
Adapter adapter = new Adapter();
adapter.targetMethod();
}
}
- .运行结果
4.2、对象适配器模式
- 对象适配器使用的是组合的方式,它把源类作为属性放入适配器类中,请看代码实例
- 定义被适配的类
/**
* 源(相当于两脚插头,也就是被适配的类)
* @author ningbeibei
*/
public class Adaptee {
public void adapteeMethod() {
System.out.println("两脚插头,被适配的类....");
}
}
- 定义目标接口
/**
* 目标(客户所期待的类,可以是一个接口抽象类具体的类)
* 相当于三孔插板
* @author ningbeibei
*/
public interface Target {
void targetMethod();
}
- 定义适配器类(类似于转换器)
注意:通过持有Adaptee属性建立与Target接口联系
/**
* 对象适配器模式(相当于转换器)
* 通过持有Adaptee属性建立与Target接口联系
* @author ningbeibei
*/
public class Adapter implements Target {
//添加属性
private Adaptee adaptee;
public Adapter (Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void targetMethod() {
//操作处理
adaptee.adapteeMethod();
//操作处理
}
}
- 测试类
/**
* 适配器模式测试类
* @author ningbeibei
*/
public class test {
public static void main(String[] args) {
//对象适配器模式
Adapter adapter = new Adapter(new Adaptee());
adapter.targetMethod();
}
}
- 测试输出
4.3、接口适配器模式(缺省适配模式)
- 接口适配器模式又称缺省模式,这种模式通过抽象类对接口进行实现,在抽象类种对接口进行默认实现。请看下面代码示例
- 定义顶层接口
/**
* 定义顶层接口
* @author ningbeibei
*/
public interface Target {
void targetMethod();
void targetMethod1();
void targetMethod2();
}
- 定义抽象类 AbstrctAdapter 并实现 Target 接口
/**
* 接口适配器模式
* 定义抽象类并实现Target接口
* @author ningbeibei
*/
public abstract class AbstrctAdapter implements Target {
//默认实现方法
public void targetMethod() {
System.out.println("默认实现");
};
//需要子类必须实现的方法
public abstract void targetMethod1();
//需要子类重写这个方法
public void targetMethod2() {
System.out.println("默认实现2");
};
}
- 定义Adapter类并继承AbstrctAdapte抽象类
- 通过接口和抽象类的结合,避免了在实现接口的子类中出现大量的“无意义”实现,这个“无意义”实现,被缓冲到了抽象类中,完美展现了代码复用(可以把抽象类理解成接口和实现类之间的缓冲)
/**
* 通过继承AbstrctAdapter抽象类
* 实现它的抽象方法和重写方法
* 这种方式是通过抽象类缓冲接口中那些我们不想实现的空方法
* @author ningbeibei
*/
public class Adapter extends AbstrctAdapter {
@Override
public void targetMethod1() {
System.out.println("子类必须实现");
}
public void targetMethod2() {
System.out.println("重写实现2");
};
}
- 测试类
/**
* 缺省模式
* @author ningbeibei
*/
public class test {
public static void main(String[] args) {
Adapter adapter = new Adapter();
adapter.targetMethod();
adapter.targetMethod1();
adapter.targetMethod2();
}
}
- 测试结果
通过上面结果输出我们可以得道以下几点:
- 提供方法默认实现
- 决定子类必须要实现方法
- 子类可以决定是否重新父类方法
- 通过接口和抽象类的结合,避免了在实现接口的子类中出现大量的“无意义”实现,这个“无意义”实现,被缓冲到了抽象类中
5、适配器模式的优缺点
5.1、优点
- 更好的复用性:系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。
- 更好的扩展性:在实现适配器功能的时候,可以扩展自己源的行为(增加方法),从而自然地扩展系统的功能。
5.2、缺点
- 会导致系统紊乱:滥用适配器,会让系统变得非常零乱。
例如,明明看到调用的是A接口,其实内部被适配成了B接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构