在现实生活中,经常出现两个对象因接口不兼容而不能在一起工作的实例,这时需要第三者进行适配。例如,讲中文的人同讲英文的人对话时需要一个翻译,用直流电的笔记本电脑接交流电源时需要一个电源适配器,用220V电压给手机充电的时候需要手机充电器将220V转为手机可以充电的5V等。
在软件设计中也可能出现:需要开发的具有某种业务功能的组件在现有的组件库中已经存在,但它们与当前系统的接口规范不兼容,如果重新开发这些组件成本又很高,这时用适配器模式能很好地解决这些问题。
适配器工作原理
- 适配器模式:将一个类的接口转成另一种接口,让原本接口不兼容的类可以兼容
- 从用户角度看不到被适配者,是解耦的
- 用户调用适配器转化出来的目标接口方法,适配器再度调用被适配者的相关接口方法
- 用户收到反馈结果,感觉只是和目标接口交互
1、类适配器模式
基本介绍:
Adapter类,通过继承src类,实现dst类接口,完成src-->dst的适配
手机充电器案例
插座电压(220V),手机充电需要的电压(5V),充电器(将220V转为5V)
充电器就相当于Adapte,220V是src,5V是dst
代码实现:
被适配者类(Voltage220V):
/**
* 被适配者类
*/
public class Voltage220V {
protected int outPut220V() {
int src = 220;
System.out.println("当前电压:" + src + "V");
return src;
}
}
目标类(IVoltage5V):
/**
* 目标类
*/
public interface IVoltage5V {
public int outPut5V();
}
适配器类(VolategeAdapter):
/**
* 适配器类
*/
public class VolategeAdapter extends Voltage220V implements IVoltage5V{
public int outPut5V() {
int srcV = outPut220V();
int dstV = srcV / 44 ;
System.out.println("电压处理,当前电压:" + dstV + "V");
return dstV;
}
}
使用者类(Phone):
public class Phone {
public void charging(IVoltage5V iVoltage5V) {
if(iVoltage5V.outPut5V() == 5){
System.out.println("电压匹配成功,开始充电");
}
}
}
客户端类(Client):
/**
* 客户端类
*/
public class Client {
public static void main(String[] args) {
System.out.println("=== 类适配器模式 ===");
Phone phone = new Phone();
phone.charging(new VolategeAdapter());
}
}
类适配器的注意事项和细节:
- Java是单继承机制,所以类适配器需要继承src类这一点算一个缺点,因为dst必须是接口,有一定局限性
- src类的方法必须在Adapter类中暴露出来,增加了使用成本
- 优点:由于继承了src类,所以它可以根据需求重写src类的方法,是的Adapter的灵活性增强了
针对于类适配器必须继承src类的缺点,提出改进方案,即对象适配器
2、对象适配器
基本介绍:
对象适配器是适配器没模式中最常用的一种,基本思路与类适配器相同,只是将Adapter类做修改,不是继承src类,而是持有src类的实例,从而解决兼容性问题。即持有src类,实现dst接口,完成src-->dst的适配。
代码实现:(在上述类适配器的基础上修改)
为了对比类适配器模式,这里我们新增一个类 ( VoltageAdapter2 ) :
/**
* 适配器类
*/
public class VolategeAdapter2 implements IVoltage5V {
// 持有 Voltage220V 类 ,不是继承
private Voltage220V voltage220V;
// 通过构造器传入 Voltage220V 实例
public VolategeAdapter2(Voltage220V voltage220v) {
this.voltage220V = voltage220v;
}
public int outPut5V() {
int srcV = voltage220V.outPut220V();
int dstV = srcV / 44;
System.out.println("电压处理,当前电压:" + dstV + "V");
return dstV;
}
}
修改客户端类(Client):
/**
* 客户端类
*/
public class Client {
public static void main(String[] args) {
System.out.println("=== 类适配器模式 ===");
Phone phone = new Phone();
phone.charging(new VolategeAdapter());
System.out.println("=== 对象适配器模式 ===");
Phone phone1 = new Phone();
phone1.charging(new VolategeAdapter2(new Voltage220V()));
}
}
对象适配器模式注意事项和细节
- 对象适配器和类适配器其实算是同一种思想,只是实现方法不同
- 对象适配器使用组合代替继承,解决了类适配器必须继承src类的局限性问题,也不再要求dst是接口
- 使用成本低,更加灵活。推荐使用
3、接口适配器模式(也叫缺省适配器模式)
基本介绍:
当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中的每一个方法提供一个默认实现(空方法),那么该抽象类的子类可以有选择的覆盖父类的某些方法来实现需求,适用于一个接口不想使用其所有的方法的情况
Interface4:
public interface Interface4 {
public void m1();
public void m2();
public void m3();
public void m4();
}
AbsAdapter:
/**
*实现接口,空实现所有方法
*/
public class AbsAdapter implements Interface4{
@Override
public void m1() {
// TODO Auto-generated method stub
}
@Override
public void m2() {
// TODO Auto-generated method stub
}
@Override
public void m3() {
// TODO Auto-generated method stub
}
@Override
public void m4() {
// TODO Auto-generated method stub
}
}
A:
public class A {
public static void main(String[] args) {
AbsAdapter absAdapter = new AbsAdapter(){
public void m1(){
System.out.println("我实现了m1方法");
}
};
absAdapter.m1(); // 需要用到的方法就自己重写父类的空实现
absAdapter.m2(); // 虽然可以调用其他方法但是都是空实现 (方法m3/m4 同理)
}
}
4、双向适配器模式
实现代码:
//客户端代码
public class TwoWayAdapterTest {
public static void main(String[] args) {
System.out.println("目标通过双向适配器访问适配者:");
TwoWayAdaptee adaptee = new AdapteeRealize();
TwoWayTarget target = new TwoWayAdapter(adaptee);
target.request();
System.out.println("-------------------");
System.out.println("适配者通过双向适配器访问目标:");
target = new TargetRealize();
adaptee = new TwoWayAdapter(target);
adaptee.specificRequest();
}
}
//目标接口
interface TwoWayTarget {
public void request();
}
// 适配者接口
interface TwoWayAdaptee {
public void specificRequest();
}
// 目标实现
class TargetRealize implements TwoWayTarget {
public void request() {
System.out.println("目标代码被调用!");
}
}
// 适配者实现
class AdapteeRealize implements TwoWayAdaptee {
public void specificRequest() {
System.out.println("适配者代码被调用!");
}
}
// 双向适配器
class TwoWayAdapter implements TwoWayTarget, TwoWayAdaptee {
private TwoWayTarget target;
private TwoWayAdaptee adaptee;
public TwoWayAdapter(TwoWayTarget target) {
this.target = target;
}
public TwoWayAdapter(TwoWayAdaptee adaptee) {
this.adaptee = adaptee;
}
public void request() {
adaptee.specificRequest();
}
public void specificRequest() {
target.request();
}
}