适配器模式,是把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配无法在一起工作的两个类可以在一起工作。
在生活中,适配器的运用也是随处可见。比如,现在中国一般的家用电器要求的电压都是220V,但是国外的电压可能是110V,那么对于常用的电器来说,从国外代购回来后怎么办?这时就需要买一个能把 220V 电压转换成 110V 电压的变压器,这个变压器就是一个适配器。比如,安卓手机和苹果手机的充电器接口不同,直接拿来使用,是没有办法通过安卓充电器给苹果手机充电的,但是现在市场上有一种”通用接头“,安卓和苹果手机都可以通过它充电,这个”通用接头“就是一个适配器。
简单来说,适配器的通用UML如图:
这是从Rational Rose 的帮助文件中截取的,这个类图也很容易理解,Target 是一个类(接口),Adaptee 是一个类(接口),通过Adapter 把Adaptee 包装成Target 的一个子类(实现类)。
通过以上UML图可以看出,适配器模式有3个重要的角色:
目标角色(Target):客户所期待的接口,目标可以是具体的或抽象的类,也可以是接口。
源角色(Adaptee):需要适配的类或适配者类。
适配器角色(Adapter):通过包装一个需要适配的对象,把原接口转换成目标接口。
适配器模式有类的适配器模式和对象的适配器模式两种不同的形式,都是把被适配的类的API转换成为目标类的API,类的适配器模式中,Adapter与Adaptee是继承关系,对象的适配器模式是使用关联关系连接到Adaptee类。
我来讲讲自己的一个经验。以前的公司,是一个软件供应商,专门给各金融机构提供服务。然而,许多时候,我们都需要与金融机构,比如银行的其他系统进行数据对接,完成清算与交易的功能。但是系统对接不是那么简单的,银行系统支持的数据结构与我们内部的是相差甚大,想要银行去更改他们的接口,他们往往是拒绝的,为什么?因为银行与很多供应商都有合作,若每个供应商都要求银行更改接口,那银行得提供N种接口,这是不妥的。银行是客户,客户就是上帝,所以我们只能自己烧脑,想办法解决这个问题。当然,我们内部的接口也是不能动的,还有其他金融机构客户在使用,怎么办?这时,适配器的作用就体现出来了,我们可以针对不同数据结构的银行,做一套适配器,将银行的接口类包装成我们内部可以使用的接口。举一个简单的例子,比如某数据功能,银行支持DBF格式读取文件,而我们内部是Excel文件,下面,就这个简单的例子分别使用类适配器模式和对象适配器模式代码实现。
1、类适配器模式
package com.pattern.adapter.v1;
/**
* 银行数据系统:只支持读取Dbf格式的文件
* @author
*
*/
public class BankDataSystem {
public void readFileDbf() {
System.out.println("--BankDataSystem----readDbf...");
}
}
package com.pattern.adapter.v1;
/**
* 内部数据接口
* @author
*
*/
public interface InnerSystem {
public void readFileExcel();
}
package com.pattern.adapter.v1;
/**
* 内部数据系统实现类:只支持读Excel格式的文件
* @author
*
*/
public class InnerDataSystem implements InnerSystem {
@Override
public void readFileExcel() {
System.out.println("-----InnerDataSystem--------readExcel-------------");
}
}
package com.pattern.adapter.v1;
/**
* 适配器:将银行数据系统的功能,包装成目标内部系统的功能
* 可以读入excel文件
* @author
*
*/
public class SystemAdapter extends BankDataSystem implements InnerSystem {//继承BankDataSystem达到适配目的
@Override
public void readFileExcel() {
System.out.println("-------------by adapter readExcel---------------");
super.readFileDbf();
}
}
package com.pattern.adapter.v1;
/**
* 客户端测试类:
* @author
*
*/
public class ClientTest {
public static void main(String[] args){
BankDataSystem system=new BankDataSystem();
system.readFileDbf();
InnerSystem adapter=new SystemAdapter();
adapter.readFileExcel();
}
}
2、对象适配器模式
package com.pattern.adapter.v2;
/**
* 银行数据系统:只支持读取Dbf格式的文件
* @author
*
*/
public class BankDataSystem {
public void readFileDbf() {
System.out.println("--BankDataSystem----readDbf...");
}
}
package com.pattern.adapter.v2;
/**
* 内部数据接口
* @author
*
*/
public interface InnerSystem {
public void readFileExcel();
}
package com.pattern.adapter.v2;
/**
* 内部数据系统实现类:只支持读Excel格式的文件
* @author
*
*/
public class InnerDataSystem implements InnerSystem {
@Override
public void readFileExcel() {
System.out.println("-----InnerDataSystem--------readExcel-------------");
}
}
package com.pattern.adapter.v2;
/**
* 适配器:将银行数据系统的功能,包装成目标内部系统的功能
* 可以读入excel文件
* @author
*
*/
public class SystemAdapter implements InnerSystem{
private BankDataSystem system;//维护BankDataSystem关联关系,达到适配目的
public SystemAdapter(BankDataSystem system) {
this.system = system;
}
@Override
public void readFileExcel() {
System.out.println("------------------by adapter readExcel-------------");
this.system.readFileDbf();
}
}
package com.pattern.adapter.v2;
/**
* 客户端测试类:
* @author
*
*/
public class ClientTest {
public static void main(String[] args){
BankDataSystem system=new BankDataSystem();
system.readFileDbf();
InnerSystem adapter=new SystemAdapter(system);
adapter.readFileExcel();
}
}
类适配器和对象适配器的特点:
1、类适配器使用对象继承的方式,是静态的定义方式;而对象适配器使用对象组合的方式,是动态组合的方式。
2、由于类适配器直接继承了Adaptee,使得适配器不能和Adaptee的子类一起工作,因为继承是静态的关系,当适配器继承了Adaptee后,就不可能再去处理 Adaptee的子类了。
3、对于类适配器,仅仅引入了一个对象,并不需要额外的引用来间接得到Adaptee。而对象适配器,需要额外的引用来间接得到Adaptee。
4、对于对象适配器,一个适配器可以把多种不同的源适配到同一个目标。换言之,同一个适配器可以把源类和它的子类都适配到目标接口。因为对象适配器采用的是对象组合的关系,只要对象类型正确,是不是子类都无所谓。
建议尽量使用对象适配器的实现方式,多用合成/聚合、少用继承,提高代码的可复用性,方便扩展。当然,具体问题具体分析,根据需要来选用实现方式,最适合的才是最好的。
适配器模式的优点与缺点:
优点:
1、系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。
2、在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。
缺点:
过多的使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是A接口,其实内部被适配成了B接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
总而言之,适配器模式不适合在系统设计阶段采用,没有一个系统分析师会在做详设的时候考虑使用适配器模式,这个模式使用的主要场景是扩展应用中,就像我们上面的那个例子一样,系统扩展了,不符合原有设计的时候才考虑通过适配器模式减少代码修改带来的风险。
源码下载:http://download.youkuaiyun.com/download/pelifymeng2/9994734