一、定义
适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。比如以下的场景:
用手机充电为例,有一个手机的插孔是TypeC口,现在只有USB的数据线,这时候充电就需要一个转接口(Adapter类)来解决充电问题。
二、基本角色
- 目标角色:这就是所期待得到的接口。比如上面的TypeC口
- 源角色:现在需要适配的接口,比如上面的USB口。
- 适配器角色:适配器类是本模式的核心。适配器把源接口转换成目标接口。显然,这一角色不可以是接口,而必须是具体类。
三、示例程序
1、类适配器模式
通过继承来实现适配器功能。当我们要访问的接口A中没有我们想要的方法 ,却在另一个接口B中发现了合适的方法,我们又不能改变访问接口A,在这种情况下,我们可以定义一个适配器p来进行中转,这个适配器p要实现我们访问的接口A,然后再继承接口B的实现类C,这时我们在适配器P中的接口A方法中直接引用C中的合适方法,这样就完成了一个简单的类适配器。
新建目标角色:
public interface ITypeC { void charge_typec(); }
新建源角色:
public interface IUsb { void charge_usb(); }
public class Usb implements IUsb { public void charge_usb() { System.out.println("我是usb接口"); } }
新建适配器:
public class Adapter extends Usb implements ITypeC { public void charge_typec() { charge_usb(); } }
测试:
public class Test { public static void main(String[] args){ ITypeC iTypeC = new Adapter(); iTypeC.charge_typec(); } }
我是usb接口
2、对象适配器模式
象适配器使用对象组合的方式,是动态组合的方式。当我们要访问的接口A中没有我们想要的方法 ,却在另一个接口B中发现了合适的方法,我们又不能改变访问接口A,在这种情况下,我们可以定义一个适配器p来进行中转,这个适配器p要实现我们访问的接口A,并且适配器要持有接口B的实例(不同于类适配器的地方),这时我们在适配器P中的接口A方法中调用接口B的实例的方法。
新建目标角色:
public interface ITypeC { void charge_typec(); }
新建源角色:
public interface IUsb { void charge_usb(); }
public class Usb implements IUsb { public void charge_usb() { System.out.println("我是usb接口"); } }
新建适配器:(注意:这里是不同于类适配器模式的地方)
public class Adapter implements ITypeC { IUsb iUsb = null; public Adapter(IUsb iUsb){ this.iUsb = iUsb; } public void charge_typec() { iUsb.charge_usb(); } }
新建测试类:
public class Test { public static void main(String[] args){ ITypeC iTypeC = new Adapter(new Usb()); iTypeC.charge_typec(); } }
我是usb接口
类适配器和对象适配器的权衡
- 类适配器使用对象继承的方式,是静态的定义方式;而对象适配器使用对象组合的方式,是动态组合的方式。
对于对象适配器一个适配器可以把多种不同的源适配到同一个目标。换言之,同一个适配器可以把源类和它的子类都适配到目标接口。因为对象适配器采用的是对象组合的关系,只要对象类型正确,是不是子类都无所谓。
对于类适配器适配器可以重定义Usb的部分行为,相当于子类覆盖父类的部分实现方法。
对于对象适配器要重定义Usb的行为比较困难,这种情况下,需要定义Usb的子类来实现重定义,然后让适配器组合子类。虽然重定义Usb的行为比较困难,但是想要增加一些新的行为则方便的很,而且新增加的行为可同时适用于所有的源。
对于类适配器,仅仅引入了一个对象,并不需要额外的引用来间接得到Usb。
对于对象适配器,需要额外的引用来间接得到Usb。