适配器模式的主要作用就是把原本不兼容的接口,通过适配修改做到统一。比如美版和港版电脑的插头与国行不一样,所以我们需要更换电源适配器,或者增加一个转换插头来解决问题。
适配器模式一般分为三种:
1.类适配器模式:
将一个类转换成满足另一个新接口的类
创建一个新类,继承原有的类,实现新的接口
2.对象适配器模式:
将一个对象转换成满足另一个新接口的对象
创建一个Wrapper类,持有原类的一个实例,在Wrapper类中调动实例的方法
3.接口适配器模式:
当不希望实现一个接口中所有的方法时
可以创建一个抽象类Wrapper,实现所有方法,我们写别的类的时候,继承抽象类。
=========================================================================
我们结合一个场景来看看具体的实现:
假设一个场景,我们现在有一个美版电脑需要充电,但是我们只有国标的充电头,如何实现一个适配充电。
=========================================================================
首先定义两个接口,一个是国标接口,一个是美标接口。
国标接口:
package AdapterPattern.Dao;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName PowerAdapterC.java
* @Description 国标接口
* @createTime 2022年03月01日 14:03:00
*/
public interface PowerAdapterC{
/**
* 插座类型
* @return 充电类型
*/
public String chargeType();
/**
* 充电功能
*/
public void charge();
}
美标接口:
package AdapterPattern.Dao;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName PowerAdapterA.java
* @Description 美标
* @createTime 2022年03月01日 14:09:00
*/
public interface PowerAdapterA {
/**
* 插座类型
* @return 充电类型
*/
public String chargeType();
/**
* 充电功能
*/
public void charge();
}
定义一个电脑类,实现充电功能:
package AdapterPattern.Dao;
import AdapterPattern.Dao.impl.PowerAdapterAImpl;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName Computer.java
* @Description 模拟电脑充电
* @createTime 2022年03月01日 14:20:00
*/
public class Computer <T>{
public void charging(T t){
if (t instanceof PowerAdapterA) {
System.out.println("开始充电");
new PowerAdapterAImpl();
PowerAdapterA powerAdapter;
powerAdapter = (PowerAdapterA) t;
powerAdapter.charge();
System.out.println(powerAdapter.chargeType());
}else {
System.out.println("接口不匹配");
}
}
}
两个实现类,实现接口:
国标实现类:
package AdapterPattern.Dao.impl;
import AdapterPattern.Dao.PowerAdapterC;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName PowerAdapterCImpl.java
* @Description 国标转化器实现类
* @createTime 2022年03月01日 14:18:00
*/
public class PowerAdapterCImpl implements PowerAdapterC {
@Override
public String chargeType() {
return "Type-C";
}
@Override
public void charge() {
System.out.println("国标充电中======");
}
}
美标实现类:
package AdapterPattern.Dao.impl;
import AdapterPattern.Dao.PowerAdapterA;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName PowerAdapterAImpl.java
* @Description 美标转换器实现类
* @createTime 2022年03月01日 14:17:00
*/
public class PowerAdapterAImpl implements PowerAdapterA {
@Override
public String chargeType() {
return "Type-A";
}
@Override
public void charge() {
System.out.println("美标充电中+++++++++");
}
}
=========================================================================
我们分别用三种适配器来实现
=========================================================================
类适配器模式:
通过多重继承目标接口和被适配者类方式来实现适配。
package AdapterPattern.ClassAdapter;
import AdapterPattern.Dao.PowerAdapterA;
import AdapterPattern.Dao.impl.PowerAdapterCImpl;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName PowerAdapterC2A.java
* @Description 国标适配器转美标
* @createTime 2022年03月01日 14:07:00
*/
public class ClassPowerAdapterC2A extends PowerAdapterCImpl implements PowerAdapterA {
//我们用C去适配A,所以集成C的实现类,并且同时实现A的接口。
@Override
public void charge() {
super.charge();
}
}
测试类:
package AdapterPattern.ClassAdapter;
import AdapterPattern.Dao.Computer;
import AdapterPattern.Dao.PowerAdapterA;
import AdapterPattern.Dao.PowerAdapterC;
import AdapterPattern.Dao.impl.PowerAdapterCImpl;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName ClassAdapterTest.java
* @Description 转换器测试
* @createTime 2022年03月01日 14:23:00
*/
public class ClassAdapterTest {
public static void main(String[] args) {
PowerAdapterA powerAdapter = new ClassPowerAdapterC2A();
Computer<Object> computer = new Computer<>();
computer.charging(powerAdapter);
}
}
我们可以看到,虽然电脑需要适配美标插头,但是经过我们的适配器,就可以用国标插头来充电了
=========================================================================
对象适配器模式
对象适配器和类适配器使用了不同的方法实现适配,对象适配器使用组合,类适配器使用继承。
package AdapterPattern.ObjectAdapter;
import AdapterPattern.Dao.PowerAdapterA;
import AdapterPattern.Dao.PowerAdapterC;
import AdapterPattern.Dao.impl.PowerAdapterCImpl;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName ObjectPowerAdapterC2A.java
* @Description 国标适配器转美标
* @createTime 2022年03月01日 14:39:00
*/
public class ObjectPowerAdapterC2A implements PowerAdapterA {
PowerAdapterC powerAdapter= new PowerAdapterCImpl();
@Override
public String chargeType() {
return powerAdapter.chargeType();
}
@Override
public void charge() {
powerAdapter.charge();
}
}
我们的适配器实现类美标充电接口,说明适配器类型是美标的,但是在适配器中创建了一个国标适配器的对象,然后返回国标对象的执行结果。(这何尝不是一种。。。。)
测试一下:
package AdapterPattern.ObjectAdapter;
import AdapterPattern.Dao.Computer;
import AdapterPattern.Dao.PowerAdapterA;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName ObjectAdapterTest.java
* @Description 对象适配器模式测试
* @createTime 2022年03月01日 14:41:00
*/
public class ObjectAdapterTest {
public static void main(String[] args) {
PowerAdapterA powerAdapter = new ObjectPowerAdapterC2A();
Computer<Object> computer = new Computer<>();
computer.charging(powerAdapter);
}
}
=========================================================================
接口适配器模式
当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求,它适用于一个接口不想使用其所有的方法的情况。
我们先写一个适配器的抽象类,并且添加两个方法(可能是实现的接口中的方法复写出来的):
package AdapterPattern.InterfaceAdapter;
import AdapterPattern.Dao.PowerAdapterA;
import AdapterPattern.Dao.PowerAdapterC;
import AdapterPattern.Dao.impl.PowerAdapterCImpl;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName InterfaceAdapterC2A.java
* @Description 适配器抽象类
* @createTime 2022年03月01日 15:18:00
*/
public abstract class InterfaceAdapterC2A implements PowerAdapterA {
PowerAdapterC powerAdapter = new PowerAdapterCImpl();
@Override
public void charge() {
powerAdapter.charge();
}
@Override
public String chargeType() {
return powerAdapter.chargeType();
}
/**
* 新方法
*/
public void b(){
System.out.println("bbbbbbbbbbbbb");
}
/**
* 新方法
*/
public void c(){
}
}
通过实现类去实现适配器,我们只需要charge和chargeType两个方法,所以我们选择性实现两个:
package AdapterPattern.InterfaceAdapter;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName InterfaceAdapterC2AImpl.java
* @Description 适配器实现类
* @createTime 2022年03月01日 15:20:00
*/
public class InterfaceAdapterC2AImpl extends InterfaceAdapterC2A {
@Override
public void charge() {
super.charge();
}
@Override
public String chargeType() {
return super.chargeType();
}
}
测试一下:
package AdapterPattern.InterfaceAdapter;
import AdapterPattern.ClassAdapter.ClassPowerAdapterC2A;
import AdapterPattern.Dao.Computer;
import AdapterPattern.Dao.PowerAdapterA;
import AdapterPattern.Dao.PowerAdapterC;
import AdapterPattern.Dao.impl.PowerAdapterCImpl;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName ClassAdapterTest.java
* @Description 转换器测试
* @createTime 2022年03月01日 14:23:00
*/
public class InterfaceAdapterTest {
public static void main(String[] args) {
PowerAdapterA powerAdapter = new InterfaceAdapterC2AImpl();
Computer<Object> computer = new Computer<>();
computer.charging(powerAdapter);
}
}
========================================================================
优点:
- 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,而无须修改原有代码。
- 增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性。
- 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。
缺点:
- 对于类适配器,JAVA不支持多继承,所以适配有一定的局限性。
- 对于对象适配器,相对类适配器,更换适配者类的方法就不容易,实现过程较为复杂。