最近学习了一些开源代码(spring,nutz),发现内面适配器模式应用的很多,如是我想整理下适配器模式的定义以及它的应用场景,方便自己以后温故而知新。
通常,客户类(clients of class)通过类的接口访问它提供的服务。有时,现有的类(existing class)可以提供客户类的功能需要,但是它所提供的接口不一定是客户类所期望的。这是由于现有的接口太详细或者缺乏详细或接口的名称与客户类所查找的不同等诸多不同原因导致的。
在这种情况下,现有的接口需要转化(convert)为客户类期望的接口,这样保证了对现有类的重用。如果不进行这样的转化,客户类就不能利用现有类所提供的功能。适配器模式(Adapter Pattern)可以完成这样的转化。适配器模式建议定义一个包装类,包装有不兼容接口的对象。这个包装类指的就是适配器(Adapter),它包装的对象就是适配者(Adaptee)。适配器提供客户类需要的接口,适配器接口的实现是把客户类的请求转化为对适配者的相应接口的调用。换句话说:当客户类调用适配器的方法时,在适配器类的内部调用适配者类的方法,这个过程对客户类是透明的,客户类并不直接访问适配者类。因此,适配器可以使由于借口不兼容而不能交互的类可以一起工作(work together)。
在上面讨论的接口:
(1) 不是指在JAVA编程语言中接口的概念,虽然类的接口可以通过JAVA借扩来定义。
(2) 不是指由窗体和GUI控件所组成的GUI应用程序的用户接口。
(3) 而是指类所报漏的,被其他类调用的编程接口,
类适配器(Class Adapter)VS对象适配器(Object Adapter)
适配器总体上可以分为两类??类适配器(Class Adapter)VS对象适配器(Object Adapter)
类适配器:
类适配器是通过继承类适配者类(Adaptee Class)实现的,另外类适配器实现客户类所需要的接口。当客户对象调用适配器类方法的时候,适配器内部调用它所继承的适配者的方法。
对象适配器:
对象适配器包含一个适配器者的引用(reference),与类适配器相同,对象适配器也实现了客户类需要的接口。当客户对象调用对象适配器的方法的时候,对象适配器调它所包含的适配器者实例的适当方法。
类适配器(Class Adapter) | 对象适配器(Object Adapter) |
基于继承概念 | 利用对象合成 |
只能应用在适配者是接口,不能利用它子类的 接口,当类适配器建立时,它就静态地与适配者关联 |
可以应用在适配者是接口和它的所有子类 |
因为适配器是作为适配者的子类,所以适配器可以能 会重载适配者的一些行为。注意:在java中,子类不能 重载父类中声明的final方法 |
不能重载适配者的方法。 注意:字面上,不能重载只是因为没有继承。 但是适配器提供包装方法可以按需要改变行为。 |
补充:
类适配器(Class Adapter) 对象适配器(Object Adapter)
基于继承概念 利用对象合成
只能应用在适配者是接口,不能利用它子类的接口,当类适配器建立时,它就静态地与适配者关联 可以应用在适配者是接口和它的所有子类,因为适配器是作为适配者的子类,所以适配器可能会重载适配者的一些行为。
注意:在JAVA中,子类不能重载父类中声明为final的方法。 不能重载适配者的方法。
注意:字面上,不能重栽只是因为没有继承。但是适配器提供包装方法可以按需要改变行为。
客户类对适配者中声明为public的接口是可见的, 客户类和适配者是完全不关联的,只有适配器才能感知适配者接口。
在JAVA应用程序中:
适用于期待的接口是JAVA接口的形式,而不是抽象地或具体地类的形式。这是因为JAVA编程语言只允许单继承。因此,类适配器设计成适配者的子类。 在JAVA应用程序中:
适用于当客户对象期望的接口是抽象类的形式,同时也可以应用于期望接口是Java接口的形式。
类适配器
客户的开发人员定义了一个接口,期望用这个接口来完成整数的求和操作,接口定义如下:
开发人员在了解这个接口的定义后,发现一个第三方类,里面有一个方法能实现他们期望的功能,其代码如下:
以上第三方类OtherOperation的方法public int otherAdd(int a,int b)所提供的功能,完全能符合客户的期望,所以只需要想办法把OtherOperation的otherAdd(int a,int b)和客户的Operation接口联系起来,让这个第三方类来为客户提供他们期望的服务就行了,这样就避免了开发人员再度去研究类似OtherOperation的otherAdd(int a,int b)方法的实现(利用已有的轮子,避免重复发明),这方法之一,就是用适配器模式:
由于java是不能实现多继承的,所以我们不能通过构建一个适配器,让他来继承所有原以完成我们的期望,这时候怎么办呢?只能用适配器的另一种实现--对象适配器:
上面代码很明显,适配器并不是通过继承来获取适配类(原)的功能的,而是通过适配类的对象来获取的,这就解决了java不能多继承所带来的不便了。这也是java提倡的编程思想之一,即尽量使用聚合不要使用继承。
还有一种情况是需要使用对象适配器的。我们来看看,
单我们的客户提供的需求并不是一个明确的接口,而是一个类,并没有定义期望的方法,如下
现在客户要一个新类B,要求能在保留类A功能的情况下增加一个运算减法的功能,并要求B能随时替换掉A但不能对已有系统造成影响。这样我们只能新建一个类B,并让B继承A。
这时候,我们发现类C已经提供了实现减法的函数,
为了避免重复去设计该函数,我们决定引入C类,通过适配C类来达到我们的期望,但问题是A和C都是一个具体类,我们无法让B同时继承这个两个类,而B继承A又是必须的,所以我们只能考虑把C给内聚到B内部,对象适配器又得派上用场了。
这样,在需要A类的地方都能用B类来代替,同时又保证了新的功能的引入。
更灵活的实现--隐藏目标接口的抽象适配器
做java 桌面应用的都知道WindowListener接口,
要实现这个接口,我们就必须实现它所定义的所有方法,但是实际上,我们很少需要同时用到所有的方法,我们要的只是其中的两三个。为了不使我们实现多余的方法,
jdk WindowListener提供了一个WindowListener的默认实现类WindowAdapter类,这是一个抽象类,
WindowAdapter类对WindowListener接口的所有有方法都提供了空实现,
有了WindowAdapter类,我们只需要去继承WindowAdapter,然后选择我们所关心的方法来实现就行了,这样就避免了直接去实现WindowListener接口。