一、引言
来看生活中这样一个适配器案例,假设小编今年经费有限,来到了泰国旅游。
这一路上奔波的手机都没电了,到了酒店就打算充会电发现泰国插座用的都是两孔的,这个国内的插头用不了呀,就只好找酒店要了个多功能转换插头(适配器),这样才把电冲上。
适配器模式(Adapter Pattern),结构型设计模式
别名为包装器,将某个类的接口转换成客户端期望的另一个接口表示,主要目的就是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。
主要分为三大类:类适配器模式、对象适配器模式、接口适配器模式。
二、适配器模式的工作原理
大家现在国内所看到的插头,电源输出一般是220V,但我们在给手机充电的时候,其实手机是不接受这个220V的电压的,所以充电器把220V的电压转换成手机所需要的电压,手机才能充电,这里设计到三个角色。
被适配者角色(Adaptee):对应的就是220V电压,这是插头默认的输出电压
适配者角色(Adapter):对应的就是手机充电器,手机充电器把22V电压,转换成手机所需要的电压
目标角色(Target):对应的是5V电压,最后手机所需要的电压。
1、将一个类的接口转换成另一个种接口,让原本接口不兼容的类可以兼容
2、从用户的角度看不到被适配者,是结耦的。
3、用户调用适配器转化出来的目标接口方法,适配器再调用被适配者相关接口方法。
4、用户接收到反馈,感觉只是和目标接口交互。
三、类适配器
我们来看看采用类适配模式,来怎么完成电压转换的问题。
1、首先我们要有一个被适配者类,来提供输出220V电压。
2、定义一个接口,规定输出5V电压,每个不同的充电器,有着不同的输出大小,这里规定的是5V。
3、定义适配器角色,实现刚刚所定义输出5V电压的接口,并且继承被适配者类获取220V的电压。
4、手机通过适配器来进行充电。
/**
* @Auther: IT贱男
* @Date: 2019/8/13 11:36
* @Description: 被适配者,输出220V电压
*/
public class Voltage200V {
public int outPut220V() {
System.out.println("被适配者 Voltage200V");
return 220;
}
}
/**
* @Auther: IT贱男
* @Date: 2019/8/13 11:38
* @Description: 定义输出5V电压接口
*/
public interface Voltage5V {
int outPut5V();
}
/**
* @Auther: IT贱男
* @Date: 2019/8/13 11:39
* @Description: 适配器,这里是继承并且实现接口,并且进行电压转换
*/
public class VoltageAdapter extends Voltage200V implements Voltage5V {
@Override
public int outPut5V() {
// 相当于电充电器插入插座的时候,获得了220V的电压
int i = outPut220V();
// 判断一下这个插座的输出的电压,符不符合充电器的标准
if (i == 220) {
// 符合标准就进行转换
return 220 / 44;
}
return 0;
}
}
/**
* @Auther: IT贱男
* @Date: 2019/8/13 11:41
* @Description: 手机
*/
public class Phone {
/**
* 充电的方法,需要传入一个5V电压的适配器,然后充电
* @param voltage5V
*/
public void Charge(Voltage5V voltage5V) {
int i = voltage5V.outPut5V();
System.out.println("获取到" + i + "V电压,可以充电");
}
}
/**
* @Auther: IT贱男
* @Date: 2019/8/13 11:42
* @Description: 客户端
*/
public class Client {
public static void main(String[] args) {
// 首先的有一个手机
Phone phone = new Phone();
// 充电的时候要有一个充电器
phone.Charge(new VoltageAdapter());
}
}
四、对象适配器
对象适配器和类适配器是同一种思想,只不过实现方式略有区别,根据合成复用原则(少用继承,多用聚合组合)原则,使用聚合来代替继承。解决了类适配器继承局限性的问题,因为java中是单一继承的,提高了使用灵活性。
主要的区别就是改动了:适配器角色,代码如下:
/**
* @Auther: IT贱男
* @Date: 2019/8/13 11:39
* @Description: 适配器
*/
public class VoltageAdapter implements Voltage5V {
// 通过构造器聚合的形式,这里直接采用的类,最好是定义成接口/父类
private Voltage200V voltage200V;
public VoltageAdapter(Voltage200V voltage200V) {
this.voltage200V = voltage200V;
}
public int outPut5V() {
if (null != voltage200V) {
return 0;
}
// 获取220V电压
int src = voltage200V.outPut220V();
if (src == 220) {
System.out.println("我是适配器:已将220V电压转换为5V电压");
return src / 44;
}
return 0;
}
}
/**
* @Auther: IT贱男
* @Date: 2019/8/13 11:42
* @Description: 客户端
*/
public class Client {
public static void main(String[] args) {
Phone phone = new Phone();
phone.Charge(new VoltageAdapter(new Voltage200V()));
}
}
五、接口适配器
那接口适配是做什么呢? 假设我们项目之前有一个接口,里面提供了三个方法。但是的要求只需要实现里面其中一个方法即可,如果按照传统的方式去实现,那肯定是三个方法都要实现,这接口适配器就来了。
首先需要创建一个抽象类,实现该接口全部方法,都是默认实现,由该抽象类的子类有选择性的覆盖其中的方法。
在JDK类库的事件处理包java.awt.event中广泛使用了接口适配器模式,如WindowAdapter、KeyAdapter、MouseAdapter等。
/**
* @Auther: IT贱男
* @Date: 2019/8/13 16:40
* @Description: 接口定义
*/
public interface InterfaceAdapter {
void method1();
void method2();
void method3();
}
/**
* @Auther: IT贱男
* @Date: 2019/8/13 16:42
* @Description: 抽象类,全部都是默认实现
*/
public abstract class AbstractAdapter implements InterfaceAdapter {
@Override
public void method1() {}
@Override
public void method2() {}
@Override
public void method3() {}
}
/**
* @Auther: IT贱男
* @Date: 2019/8/13 16:43
* @Description:
*/
public class Client {
public static void main(String[] args) {
// 可以使用采用子类继承,选择重写部分方法,这里小编直接这样写了
AbstractAdapter method = new AbstractAdapter() {
@Override
public void method1() {
System.out.println("method1");
}
};
method.method1();
}
}
六、适配器注意事项
类适配器:采用继承
对象适配器:采用聚合组合
接口适配器:使用抽象类进行接口默认实现,子类有选择的进行覆盖实现
适配器模式最大的作用就是将原本不兼容的接口融合在一起工作,在实际开发中可根据实际情况来选择。