目录
一、概述
将一个类的接口转换成客户希望的另外一个接口,适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
适配器模式有两种:类适配器、对象适配器、接口适配器。前二者在实现上有些许区别,作用一样,第三个接口适配器差别较大。
优点:
1、将目标类和适配者类解耦;
2、增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性;
3、灵活性和扩展性都非常好,符合开闭原则;
角色:
目标(Target):定义一个客户端使用的特定接口。
客户端(Client):使用目标接口,与和目标接口一致的对象合作。
被适配者(Adaptee):一个现存需要适配的接口。
适配器(Adapter):负责将Adaptee的接口转换为Target的接口。适配器是一个具体的类,这是该模式的核心。
二、类适配器模式
所谓类适配器,指的是适配器Adapter继承我们的被适配者Adaptee,并实现目标接口Target。由于Java中是单继承,所以这个适配器仅仅只能服务于所继承的被适配者Adaptee。
原理:通过继承来实现适配器功能。
当我们要访问的接口A中没有我们想要的方法 ,却在另一个接口B中发现了合适的方法,我们又不能改变访问接口A,在这种情况下,我们可以定义一个适配器p来进行中转,这个适配器p要实现我们访问的接口A,这样我们就能继续访问当前接口A中的方法(虽然它目前不是我们的菜),然后再继承接口B的实现类BB,这样我们可以在适配器P中访问接口B的方法了,这时我们在适配器P中的接口A方法中直接引用BB中的合适方法,这样就完成了一个简单的类适配器。
假设现在有个type-c充电器接口头子,但是手机是正常的usb接口头子,这个时候需要一个转化器将type-c接口换成usb接口,手机就可以充电了。
被适配者,typeC接口
public interface TypeC {
public void isTypeC();
}
目标接口,usb接口
public interface Usb {
public void isUsb();
}
假设我们的手机最终只能对接到usb接口,所以实现usb接口,而不实现typeC接口
public class UsbImpl implements Usb{
@Override
public void isUsb() {
System.out.println("这是usb接口");
}
}
适配器,将调用typeC的接口,适配到usb接口上。通过继承UsbImpl来实现适配器功能。
public class Adapter extends UsbImpl implements TypeC {
@Override
public void isTypeC() {
isUsb();
}
}
测试类
public class ClientMain {
public static void main(String[] args) {
TypeC typeC = new Adapter();
typeC.isTypeC();
}
}
输出
这是usb接口
三、对象适配器模式
所谓对象适配器,简单的说就是适配器实现我们的目标接口,但是并不继承需要被适配的类。而是通过在适配器的构造函数中将需要被适配的类传递进来从而进行适配。对象适配器可以适配多个带适配的类。只需要你在Adapter的构造方法中传递不同的带适配的类即可。具有灵活性。
原理:通过组合来实现适配器功能。
当我们要访问的接口A中没有我们想要的方法,却在另一个接口B中发现了合适的方法,我们又不能改变访问接口A,在这种情况下,我们可以定义一个适配器p来进行中转,这个适配器p要实现我们访问的接口A,这样我们就能继续访问当前接口A中的方法(虽然它目前不是我们的菜),然后在适配器P中定义私有变量C(对象)(B接口指向变量名),再定义一个带参数的构造器用来为对象C赋值,再在A接口的方法实现中使用对象C调用其来源于B接口的方法。
代码基本与上面一样,只是在适配器不用继承适配目标累了,而是直接实现接口,然后调用对象的方法。
public class Adapter implements Ps2 {
private Usb usb;
public Adapter(Usb usb){
this.usb = usb;
}
@Override
public void isTypec() {
usb.isUsb();
}
}
四、接口适配器模式
原理:通过抽象类来实现适配,这种适配稍别于上面所述的适配。
当存在这样一个接口,其中定义了N多的方法,而我们现在却只想使用其中的一个到几个方法,如果我们直接实现接口,那么我们要对所有的方法进行实现,哪怕我们仅仅是对不需要的方法进行置空(只写一对大括号,不做具体方法实现)也会导致这个类变得臃肿,调用也不方便,这时我们可以使用一个抽象类作为中间件,即适配器,用这个抽象类实现接口,而在抽象类中所有的方法都进行置空,那么我们在创建抽象类的继承类,而且重写我们需要使用的那几个方法即可。
目标接口:A
public interface A {
void a();
void b();
void c();
void d();
void e();
void f();
}
适配器:Adapter
public abstract class Adapter implements A {
public void a(){}
public void b(){}
public void c(){}
public void d(){}
public void e(){}
public void f(){}
}
public class Ashili extends Adapter {
public void a(){
System.out.println("实现A方法被调用");
}
public void d(){
System.out.println("实现d方法被调用");
}
}
public class Clienter {
public static void main(String[] args) {
A a = new Ashili();
a.a();
a.d();
}
}
五、适用场景
类适配器与对象适配器的使用场景一致,仅仅是实现手段稍有区别,二者主要用于如下场景:
(1)想要使用一个已经存在的类,但是它却不符合现有的接口规范,导致无法直接去访问,这时创建一个适配器就能间接去访问这个类中的方法。
(2)我们有一个类,想将其设计为可重用的类(可被多处访问),我们可以创建适配器来将这个类来适配其他没有提供合适接口的类。
以上两个场景其实就是从两个角度来描述一类问题,那就是要访问的方法不在合适的接口里,一个从接口出发(被访问),一个从访问出发(主动访问)。
接口适配器使用场景:
想要使用接口中的某个或某些方法,但是接口中有太多方法,我们要使用时必须实现接口并实现其中的所有方法,可以使用抽象类来实现接口,并不对方法进行实现(仅置空),然后我们再继承这个抽象类来通过重写想用的方法的方式来实现。这个抽象类就是适配器。
参考
https://www.jb51.net/article/100146.htm
https://www.cnblogs.com/V1haoge/p/6479118.html