对于适配器相信不会陌生,生活中的例子比比皆是,像耳机转接线,充电器适配器,水管适配接口等等。经过类比很容易理解软件中的适配器模式。java
客户端须要一个target(目标)接口,可是不能直接重用已经存在的adaptee(适配者)类,由于它的接口和target接口不一致,因此须要adapter(适配器)将adaptee转换为target接口。前提是target接口和已存在的适配者adaptee类所作的事情是相同或类似,只是接口不一样且都不易修改。若是在设计之初,最好不要考虑这种设计模式。凡事都有例外,就是设计新系统的时候考虑使用第三方组件,由于不必为了迎合第三方组件修改本身的软件设计风格,能够尝试使用适配器模式。数据库
下面是一个很是典型的使用适配器模式的场景:编程
Sun公司在1996年公开了Java语言的数据库链接工具JDBC,JDBC使得Java语言程序可以与数据库链接,并使用SQL语言来查询和操做数据。JDBC给出一个客户端通用的抽象接口,每个具体数据库厂商(如SQL Server、Oracle、MySQL等)的JDBC驱动软件都是一个介于JDBC接口和数据库引擎接口之间的适配器软件。抽象的JDBC接口和各个数据库引擎API之间都须要相应的适配器软件,这就是为各个不一样数据库引擎准备的驱动程序。设计模式
另一个比较典型的适配器场景J2EE规范与J2EE规范实现的服务器。SUN公司提供了一套J2EE规范,而后不一样厂商根据本身的理解实现了不一样的应用服务器。SUN公司提供了一套servlet api规范,而后实现这套规范的著名应用服务器有Apache Tomcat、Jetty、Oracle 的 Weblogic、IBM 的 WebSphere 等。api
适配器模式的UML类图以下服务器
从类图上看主要包含以下角色:ide
类适配器模式(class adapter pattern)
经过继承进行适配(类间继承)。类适配器模式在编译时实现target(目标)接口。这种适配器模式使用了多个实现了期待的接口或者已经存在的接口的多态接口。比较典型的就是:target接口被建立为一个纯粹的接口,Java不支持多继承的语言。工具
Target:Target目标角色,该角色定义把其余类转换为什么种接口,也就是指望接口,一般状况下是一个接口或一个抽象类,通常不会是实现类测试
public interface Target { public void request(); }
Adaptee:Adaptee源角色,想把谁转换为目标角色,这个“谁”就是源角色,它是已经存在的、运行良好的类或对象this
public class Adaptee { public void specificRequest() { System.out.println("我是已经存在的运行良好的第三方厂商"); } }
Adapter:Adapter适配器角色,是适配器模式的核心角色,它的职责是经过继承或是类关联的方式把源角色转换为目标角色
public class Adapter extends Adaptee implements Target { @Override public void request() { super.specificRequest(); } }
ConcreteTarget:目标角色的实现类
public class ConcreteTarget implements Target { @Override public void request() { System.out.println("没有增长适配器的我方普通实现逻辑"); } }
类适配器模式测试代码
public class Client { public static void main(String[] args) { //原有无适配器的业务逻辑 Target target = new ConcreteTarget(); target.request(); //增长适配器后的业务逻辑 Target target2 = new Adapter(); target2.request(); } }
对象适配器模式(object adapter pattern)
经过对象层次的关联关系进行委托(对象的合成关系/关联关系)。对象适配器模式在运行时实现target(目标)接口。在这种适配器模式中,适配器包装了一个类实例。在这种状况下,适配器调用包装对象实例的方法。
Target:客户所期待的接口。目标能够是具体的或抽象的类,也能够是接口
public class Target { public void request() { System.out.println("没有适配器的普通请求"); } }
Adaptee:须要适配的类
public class Adaptee { public void specificRequest() { System.out.println("适配器类实现的特殊请求"); } }
Adapter:经过在内部包装一个Adaptee对象,把源接口转换成目标接口
public class Adapter extends Target { private Adaptee adaptee = new Adaptee(); @Override public void request() { //替换原理的逻辑,调用适配类的逻辑 adaptee.specificRequest(); } }
对象适配器模式测试代码
public class Client { public static void main(String[] args) { Target target = new Adapter(); target.request(); } }
缺省适配器模式(default adapter pattern),也叫默认适配器模式、接口适配器模式
当不须要所有实现接口提供的方法时,能够设计一个适配器抽象类实现接口,并为接口中的每一个方法提供默认方法实现或者空实现(若是你们作过GUI编程,就能够常常遇到这种实现,特别是各类控件的事件监听都提供了适配器类),抽象类的子类就能够有选择的覆盖父类的某些方法实现需求,它适用于一个接口不想使用全部的方法的状况。在java8后,接口中能够有default方法,就不须要这种缺省适配器模式了。接口中方法都设置为default,实现为空,这样一样一样能够达到缺省适配器模式一样的效果。
target:包含了不少没有实现的操做接口
public interface Target { public abstract void operation1(); public abstract void operation2(); public abstract void operation3(); }
Adapter:默认实现了全部操做抽象类,只是全部的实现都是空实现
public abstract class DefaultAdapter implements Target{ @Override public void operation1() { } @Override public void operation2() { } @Override public void operation3() { } }
测试缺省适配器模式须要用到的类(至关于GUI编程的一个组件,好比按钮Button)
public class Operator { private Target target; public void addOperation(Target target) { this.target= target; } public void operation1() { target.operation1(); } public void operation2() { target.operation2(); } public void operation3() { target.operation3(); } }
缺省适配器模式测试代码
public class Client{ public static void main(String[] args) { // 原来要实现全部操做类的操做 Operator operator1= new Operator(); operator1.addOperation(new Target() { @Override public void operation1() {} @Override public void operation2() { System.out.println("invoke operation2"); } @Override public void operation3() {} }); operator1.operation2(); // 二、使用缺省适配器只须要实现须要用到的接口方法 Operator operator2 = new Operator(); operator2.addOperation(new DefaultAdapter() { @Override public void operation2() { System.out.println("invoke operation2"); } }); operator2.operation2(); } }
适配器模式本质上是现有的不兼容的接口转换为须要的接口。类适配器模式以继承现有类的方式转换;对象适配器模式以聚合对象实例的方式转换;接口适配器模式以实现接口的方式转换。适配器模式是在现有的类和系统都不易修改的状况下才使用,在系统设计之初慎用该设计模式。