引入实例
说起适配器其实在我们的生活中是非常常见的,比如:学校的宿舍的电压都比较低,而有的学生想使用大功率电器,宿舍的就会跳闸,然而如果你使用一个适配器(变压器)就可以使用了(温馨提示宿舍使用大功率电器不太安全,容易引起火灾,希望大家谨慎使用)。
又比如说,有的插座都是三孔的,而我们用的大部分电器是两孔的,这时我们可以使用一个适配器,适配器本身是三孔的,它可以直接插到三孔的插头上,适配器本身可以提供一个两孔的插座,然后我们就可以插到适配器上了,这样我们原本只能插到两孔上的插头就能用三孔的插座了。
适配器模式的相关概念
适配器模式的正式定义
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。
适配器模式分为
- 类适配器模式
- 对象适配器模式
适配器模式的结构
适配器模式里面总共拥有三个角色,它们分别是:
- 目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
- 适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
- 适配器(Adapter)类:它是一个转换器,通过继承(类适配器模式)或引用适配者的对象(对象适配器模式),把适配者接口转换成目标接口(也就是使用转换器将三头的插座转换成适合我们使用的两头插座),让客户按目标接口的格式访问适配者。
注意事项:适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。
类适配器
类适配器的原理就是通过继承来实现适配器功能。
让Adapter实现Target接口,并且继承Adaptee,这样Adapter就具备Target和Adaptee的特性,就可以将两者进行转化。
举例:以不同设备使用不同交流电为例,通过电源适配器进行转换说明。
创建目标角色(Target)
public interface Target {
int out();
}
创建源角色(Adaptee)
public class Adaptee{
public int input() {
System.out.println("输入交流电: 220V");
return 220;
}
}
创建适配器(Adapter)
public class Adapter extends Adaptee implements Target {
@Override
public int out() {
int input220V = super.input();
int output = input220V / 2;
System.out.println("输出交流电: " + output + "V");
return output;
}
}
客户端调用
public static void main(String[] args) {
Target adapter = new Adapter();
int result = adapter.out();
System.out.println(result);
}
输入交流电: 220V
输出交流电: 110V
110
对象适配器
对象适配器的原理就是通过组合来实现适配器功能。
让Adapter实现Target接口,然后内部持有Adaptee实例,然后再Target接口规定的方法内转换Adaptee。
创建目标角色(Target)
public interface Target {
int out();
}
创建源角色(Adaptee)
public class Adaptee{
public int input() {
System.out.println("输入交流电: 220V");
return 220;
}
}
创建适配器(Adapter)
public class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public int out() {
int output = adaptee.input() / 2;
System.out.println("输出交流电: " + output + "V");
return output;
}
}
客户端调用
public static void main(String[] args) {
Target adapter = new Adapter(new Adaptee());
int result = adapter.out();
System.out.println(result);
}
输入交流电: 220V
输出交流电: 110V
110
接口适配器
接口适配器的使用场景是解决接口方法过多,如果直接实现接口,那么类会多出许多空实现的方法,类显得很臃肿。此时,使用接口适配器就能让我们只实现我们需要的接口方法,目标更清晰。
接口适配器的主要原理就是原理利用抽象类实现接口,并且空实现接口众多方法。
创建目标角色(Target)
public interface Target {
int out1();
int out2();
int out3();
int out4();
}
创建源角色(Adaptee)
public class Adaptee{
public int input() {
System.out.println("输入交流电: 220V");
return 220;
}
}
创建适配器(Adapter)
public class Adapter implements Target {
protected Adaptee adaptee;
public Adapter(Adaptee adaptee){
this.adaptee = adaptee;
}
@Override
public int out1() {
int input220V = adaptee.input();
int output = input220V / 1;
System.out.println("输出交流电: " + output + "V");
return output;
}
@Override
public int out2() {
int input220V = adaptee.input();
int output = input220V / 2;
System.out.println("输出交流电: " + output + "V");
return output;
}
@Override
public int out3() {
return 0;
}
@Override
public int out4() {
return 0;
}
}
客户端调用
public static void main(String[] args) {
Target adapter = new Adapter(new Adaptee());
adapter.out1();
System.out.println("---------------------");
adapter.out2();
System.out.println("---------------------");
Target adapter2 = new Adapter(new Adaptee()) {
@Override
public int out3() {
int input220V = adaptee.input();
int output = input220V / 3;
System.out.println("输出交流电: " + output + "V");
return output;
}
};
adapter2.out3();
System.out.println("---------------------");
Target adapter3 = new Adapter(new Adaptee()) {
@Override
public int out4() {
int input220V = adaptee.input();
int output = input220V / 4;
System.out.println("输出交流电: " + output + "V");
return output;
}
};
adapter3.out4();
}
输入交流电: 220V
输出交流电: 220V
---------------------
输入交流电: 220V
输出交流电: 110V
---------------------
输入交流电: 220V
输出交流电: 73V
---------------------
输入交流电: 220V
输出交流电: 55V
优缺点
适配器模式优点:
- 可以让任何两个没有关联的类一起运行。
- 提高了类的复用。
- 增加了类的透明度。
- 灵活性好。
适配器模式缺点:
- 过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
- 由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。
优点 :
- 类适配器模式优点:由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。
- 对象适配器模式优点:一个对象适配器可以把多个不同的适配者适配到同一个目标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口。
缺点:
- 类适配器模式缺点:对于Java、C#等不支持多重继承的语言,一次最多只能适配一个适配者类,而且目标抽象类只能为抽象类,不能为具体类,其使用有一定的局限性,不能将一个适配者类和它的子类都适配到目标接口。
- 对象适配器模式缺点:与类适配器模式相比,要想置换适配者类的方法就不容易。如果一定要置换掉适配者类的一个或多个方法,就只好先做一个适配者类的子类,将适配者类的方法置换掉,然后再把适配者类的子类当做真正的适配者进行适配,实现过程较为复杂。
类适配器和对象适配器的区别:
- 前者类之间的耦合度比后者高(这是因为类适配器模式使用的是继承的方式,而对象适配器模式使用的是聚合或者组合的方式)。
- 类适配器模式要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些,用的更多的还是对象适配器模式。
应用场景
- 当一个系统需要使用另一个系统的接口时,但是两个系统的接口不兼容,可以使用适配器模式进行接口转换。
- 在数据处理方面,适配器模式可以用于将不同格式的数据适配到一个标准的数据格式上。
- 当我们需要复用一些已有的类的时候,这些类的接口与我们需要的接口不兼容,可以使用适配器将这些类的接口转换成我们需要的接口,从而实现类的复用。
- 适配器模式可以用于封装有缺陷的接口设计,使得客户可以通过适配器来使用这些缺陷的接口,而无需直接与其交互。
- 替换依赖的外部系统,使得系统可以在不修改源代码的情况下适应新的外部系统。