适配器模式
适配器模式(Adapter Pattern):将一个接口转换成客户但愿的另外一个接口,使接口不兼容的那些类能够一块儿工做,其别名为包装器(Wrapper)。适配器模式既能够做为类结构型模式,也能够做为对象结构型模式。css
在适配器模式中,咱们经过增长一个新的适配器类来解决接口不兼容的问题,使得本来没有任何关系的类能够协同工做。java
根据适配器类与适配者类的关系不一样,适配器模式可分为对象适配器和类适配器两种,在对象适配器模式中,适配器与适配者之间是关联关系;在类适配器模式中,适配器与适配者之间是继承(或实现)关系。spring
角色
Target(目标抽象类):目标抽象类定义客户所需接口,能够是一个抽象类或接口,也能够是具体类。sql
Adapter(适配器类):适配器能够调用另外一个接口,做为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它经过继承Target并关联一个Adaptee对象使两者产生联系。数据库
Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口须要适配,适配者类通常是一个具体类,包含了客户但愿使用的业务方法,在某些状况下可能没有适配者类的源代码。设计模式
缺省适配器模式(Default Adapter Pattern):当不须要实现一个接口所提供的全部方法时,可先设计一个抽象类实现该接口,并为接口中每一个方法提供一个默认实现(空方法),那么该抽象类的子类能够选择性地覆盖父类的某些方法来实现需求,它适用于不想使用一个接口中的全部方法的状况,又称为单接口适配器模式。缺省适配器模式是适配器模式的一种变体,其应用也较为普遍。在JDK类库的事件处理包java.awt.event中普遍使用了缺省适配器模式,如WindowAdapter、KeyAdapter、MouseAdapter等。微信
示例
类适配器
首先有一个已存在的将被适配的类mvc
public class Adaptee {
public void adapteeRequest() {
System.out.println("被适配者的方法");
}
}
定义一个目标接口app
public interface Target {
void request();
}
怎么才能够在目标接口中的 request()
调用 Adaptee
的 adapteeRequest()
方法呢?框架
若是直接实现 Target
是不行的
public class ConcreteTarget implements Target {
@Override
public void request() {
System.out.println("concreteTarget目标方法");
}
}
若是经过一个适配器类,实现 Target
接口,同时继承了 Adaptee
类,而后在实现的 request()
方法中调用父类的 adapteeRequest()
便可实现
public class Adapter extends Adaptee implements Target{
@Override
public void request() {
//...一些操做...
super.adapteeRequest();
//...一些操做...
}
}
咱们来测试一下
public class Test {
public static void main(String[] args) {
Target target = new ConcreteTarget();
target.request();
Target adapterTarget = new Adapter();
adapterTarget.request();
}
}
输出
concreteTarget目标方法
被适配者的方法

这样咱们便可在新接口 Target
中适配旧的接口或类
对象适配器
对象适配器与类适配器不一样之处在于,类适配器经过继承来完成适配,对象适配器则是经过关联来完成,这里稍微修改一下 Adapter
类便可将转变为对象适配器
public class Adapter implements Target{
// 适配者是对象适配器的一个属性
private Adaptee adaptee = new Adaptee();
@Override
public void request() {
//...
adaptee.adapteeRequest();
//...
}
}

注意这里的 Adapter
是将 Adaptee
做为一个成员属性,而不是继承它
电压适配器
再来一个好理解的例子,咱们国家的民用电都是 220V,日本是 110V,而咱们的手机充电通常须要 5V,这时候要充电,就须要一个电压适配器,将 220V 或者 100V 的输入电压变换为 5V 输出
定义输出交流电接口,输出220V交流电类和输出110V交流电类
public interface AC {
int outputAC();
}
public class AC110 implements AC {
public final int output = 110;
@Override
public int outputAC() {
return output;
}
}
public class AC220 implements AC {
public final int output = 220;
@Override
public int outputAC() {
return output;
}
}
适配器接口,其中 support()
方法用于检查输入的电压是否与适配器匹配,outputDC5V()
方法则用于将输入的电压变换为 5V 后输出
public interface DC5Adapter {
boolean support(AC ac);
int outputDC5V(AC ac);
}
实现中国变压适配器和日本变压适配器
public class ChinaPowerAdapter implements DC5Adapter {
public static final int voltage = 220;
@Override
public boolean support(AC ac) {
return (voltage == ac.outputAC());
}
@Override
public int outputDC5V(AC ac) {
int adapterInput = ac.outputAC();
//变压器...
int adapterOutput = adapterInput / 44;
System.out.println("使用ChinaPowerAdapter变压适配器,输入AC:" + adapterInput + "V" + ",输出DC:" + adapterOutput + "V");
return adapterOutput;
}
}
public class JapanPowerAdapter implements DC5Adapter {
public static final int voltage = 110;
@Override
public boolean support(AC ac) {
return (voltage == ac.outputAC());
}
@Override
public int outputDC5V(AC ac) {
int adapterInput = ac.outputAC();
//变压器...
int adapterOutput = adapterInput / 22;
System.out.println("使用JapanPowerAdapter变压适配器,输入AC:" + adapterInput + "V" + ",输出DC:" + adapterOutput + "V");
return adapterOutput;
}
}
测试,准备中国变压适配器和日本变压适配器各一个,定义一个方法能够根据电压找到合适的变压器,而后进行测试
public class Test {
private List<DC5Adapter> adapters = new LinkedList<DC5Adapter>();
public Test() {
this.adapters.add(new ChinaPowerAdapter());
this.adapters.add(new JapanPowerAdapter());
}
// 根据电压找合适的变压器
public DC5Adapter getPowerAdapter(AC ac) {
DC5Adapter adapter = null;
for (DC5Adapter ad : this.adapters) {
if (ad.support(ac)) {
adapter = ad;
break;
}
}
if (adapter == null){
throw new IllegalArgumentException("没有找到合适的变压适配器");
}
return adapter;
}
public static void main(String[] args) {
Test test = new Test();
AC chinaAC = new AC220();
DC5Adapter adapter = test.getPowerAdapter(chinaAC);
adapter.outputDC5V(chinaAC);
// 去日本旅游,电压是 110V
AC japanAC = new AC110();
adapter = test.getPowerAdapter(japanAC);
adapter.outputDC5V(japanAC);
}
}
输出
使用ChinaPowerAdapter变压适配器,输入AC:220V,输出DC:5V
使用JapanPowerAdapter变压适配器,输入AC:110V,输出DC:5V
适配器模式总结
主要优势:
将目标类和适配者类解耦,经过引入一个适配器类来重用现有的适配者类,无须修改原有结构。
增长了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,并且提升了适配者的复用性,同一个适配者类能够在多个不一样的系统中复用。
灵活性和扩展性都很是好,经过使用配置文件,能够很方便地更换适配器,也能够在不修改原有代码的基础上增长新的适配器类,彻底符合“开闭原则”。
具体来讲,类适配器模式还有以下优势:
因为适配器类是适配者类的子类,所以能够在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。
对象适配器模式还有以下优势:
一个对象适配器能够把多个不一样的适配者适配到同一个目标;
能够适配一个适配者的子类,因为适配器和适配者之间是关联关系,根据“里氏代换原则”,适配者的子类也可经过该适配器进行适配。
类适配器模式的缺点以下:
对于Java、C#等不支持多重类继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适配者;
适配者类不能为最终类,如在Java中不能为final类,C#中不能为sealed类;
在Java、C#等语言中,类适配器模式中的目标抽象类只能为接口,不能为类,其使用有必定的局限性。
对象适配器模式的缺点以下:
与类适配器模式相比,要在适配器中置换适配者类的某些方法比较麻烦。若是必定要置换掉适配者类的一个或多个方法,能够先作一个适配者类的子类,将适配者类的方法置换掉,而后再把适配者类的子类当作真正的适配者进行适配,实现过程较为复杂。
适用场景:
系统须要使用一些现有的类,而这些类的接口(如方法名)不符合系统的须要,甚至没有这些类的源代码。
想建立一个能够重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在未来引进的类一块儿工做。
源码分析适配器模式的典型应用
spring AOP中的适配器模式
在Spring的Aop中,使用的 Advice(通知)
来加强被代理类的功能。
Advice
的类型有:MethodBeforeAdvice
、AfterReturningAdvice
、ThrowsAdvice
,在每一个类型 Advice
都有对应的拦截器,MethodBeforeAdviceInterceptor
、AfterReturningAdviceInterceptor
、ThrowsAdviceInterceptor
,Spring须要将每一个 Advice
都封装成对应的拦截器类型,返回给容器,因此须要使用适配器模式对 Advice
进行转换。
三个适配者类 Adaptee 以下:
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method var1, Object[] var2, @Nullable Object var3) throws Throwable;
}
public interface AfterReturningAdvice extends AfterAdvice {
void afterReturning(@Nullable Object var1, Method var2, Object[] var3, @Nullable Object var4) throws Throwable;
}
public interface ThrowsAdvice extends AfterAdvice {
}
目标接口 Target,有两个方法,一个判断 Advice
类型是否匹配,一个是工厂方法,建立对应类型的 Advice
对应的拦截器
public interface AdvisorAdapter {
boolean supportsAdvice(Advice var1);
MethodInterceptor getInterceptor(Advisor var1);
}
三个适配器类 Adapter 分别以下,注意其中的 Advice、Adapter、Interceptor之间的对应关系
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof MethodBeforeAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
@SuppressWarnings("serial")
class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof AfterReturningAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();
return new AfterReturningAdviceInterceptor(advice);
}
}
class ThrowsAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof ThrowsAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
return new ThrowsAdviceInterceptor(advisor.getAdvice());
}
}
客户端 DefaultAdvisorAdapterRegistry
public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {
private final List<AdvisorAdapter> adapters = new ArrayList(3);
public DefaultAdvisorAdapterRegistry() {
// 这里注册了适配器
this.registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
this.registerAdvisorAdapter(new AfterReturningAdviceAdapter());
this.registerAdvisorAdapter(new ThrowsAdviceAdapter());
}
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List<MethodInterceptor> interceptors = new ArrayList(3);
Advice advice = advisor.getAdvice();
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor)advice);
}
Iterator var4 = this.adapters.iterator();
while(var4.hasNext()) {
AdvisorAdapter adapter = (AdvisorAdapter)var4.next();
if (adapter.supportsAdvice(advice)) { // 这里调用适配器方法
interceptors.add(adapter.getInterceptor(advisor)); // 这里调用适配器方法
}
}
if (interceptors.isEmpty()) {
throw new UnknownAdviceTypeException(advisor.getAdvice());
} else {
return (MethodInterceptor[])interceptors.toArray(new MethodInterceptor[0]);
}
}
// ...省略...
}
这里看 while 循环里,逐个取出注册的适配器,调用 supportsAdvice()
方法来判断 Advice
对应的类型,而后调用 getInterceptor()
建立对应类型的拦截器

这里应该属于对象适配器模式,关键字 instanceof
可当作是 Advice
的方法,不过这里的 Advice
对象是从外部传进来,而不是成员属性
spring JPA中的适配器模式
在Spring的ORM包中,对于JPA的支持也是采用了适配器模式,首先定义了一个接口的 JpaVendorAdapter
,而后不一样的持久层框架都实现此接口。
jpaVendorAdapter:用于设置实现厂商JPA实现的特定属性,如设置Hibernate的是否自动生成DDL的属性generateDdl;这些属性是厂商特定的,所以最好在这里设置;目前Spring提供 HibernateJpaVendorAdapter
、OpenJpaVendorAdapter
、EclipseLinkJpaVendorAdapter
、TopLinkJpaVendorAdapter
四个实现。其中最重要的属性是 database,用来指定使用的数据库类型,从而能根据数据库类型来决定好比如何将数据库特定异常转换为Spring的一致性异常,目前支持以下数据库(DB二、DERBY、H二、HSQL、INFORMIX、MYSQL、ORACLE、POSTGRESQL、SQL_SERVER、SYBASE)
public interface JpaVendorAdapter
{
// 返回一个具体的持久层提供者
public abstract PersistenceProvider getPersistenceProvider();
// 返回持久层提供者的包名
public abstract String getPersistenceProviderRootPackage();
// 返回持久层提供者的属性
public abstract Map<String, ?> getJpaPropertyMap();
// 返回JpaDialect
public abstract JpaDialect getJpaDialect();
// 返回持久层管理器工厂
public abstract Class<? extends EntityManagerFactory> getEntityManagerFactoryInterface();
// 返回持久层管理器
public abstract Class<? extends EntityManager> getEntityManagerInterface();
// 自定义回调方法
public abstract void postProcessEntityManagerFactory(EntityManagerFactory paramEntityManagerFactory);
}
咱们来看其中一个适配器实现类 HibernateJpaVendorAdapter
public class HibernateJpaVendorAdapter extends AbstractJpaVendorAdapter {
//设定持久层提供者
private final PersistenceProvider persistenceProvider;
//设定持久层方言
private final JpaDialect jpaDialect;
public HibernateJpaVendorAdapter() {
this.persistenceProvider = new HibernatePersistence();
this.jpaDialect = new HibernateJpaDialect();
}
//返回持久层方言
public PersistenceProvider getPersistenceProvider() {
return this.persistenceProvider;
}
//返回持久层提供者
public String getPersistenceProviderRootPackage() {
return "org.hibernate";
}
//返回JPA的属性
public Map<String, Object> getJpaPropertyMap() {
Map jpaProperties = new HashMap();
if (getDatabasePlatform() != null) {
jpaProperties.put("hibernate.dialect", getDatabasePlatform());
} else if (getDatabase() != null) {
Class databaseDialectClass = determineDatabaseDialectClass(getDatabase());
if (databaseDialectClass != null) {
jpaProperties.put("hibernate.dialect",
databaseDialectClass.getName());
}
}
if (isGenerateDdl()) {
jpaProperties.put("hibernate.hbm2ddl.auto", "update");
}
if (isShowSql()) {
jpaProperties.put("hibernate.show_sql", "true");
}
return jpaProperties;
}
//设定数据库
protected Class determineDatabaseDialectClass(Database database)
{
switch (1.$SwitchMap$org$springframework$orm$jpa$vendor$Database[database.ordinal()])
{
case 1:
return DB2Dialect.class;
case 2:
return DerbyDialect.class;
case 3:
return H2Dialect.class;
case 4:
return HSQLDialect.class;
case 5:
return InformixDialect.class;
case 6:
return MySQLDialect.class;
case 7:
return Oracle9iDialect.class;
case 8:
return PostgreSQLDialect.class;
case 9:
return SQLServerDialect.class;
case 10:
return SybaseDialect.class; }
return null;
}
//返回JPA方言
public JpaDialect getJpaDialect() {
return this.jpaDialect;
}
//返回JPA实体管理器工厂
public Class<? extends EntityManagerFactory> getEntityManagerFactoryInterface() {
return HibernateEntityManagerFactory.class;
}
//返回JPA实体管理器
public Class<? extends EntityManager> getEntityManagerInterface() {
return HibernateEntityManager.class;
}
}
配置文件中能够这样指定
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="false" />
<property name="database" value="HSQL"/>
</bean>
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
spring MVC中的适配器模式
Spring MVC中的适配器模式主要用于执行目标 Controller
中的请求处理方法。
在Spring MVC中,DispatcherServlet
做为用户,HandlerAdapter
做为指望接口,具体的适配器实现类用于对目标类进行适配,Controller
做为须要适配的类。
为何要在 Spring MVC 中使用适配器模式?Spring MVC 中的 Controller
种类众多,不一样类型的 Controller
经过不一样的方法来对请求进行处理。若是不利用适配器模式的话,DispatcherServlet
直接获取对应类型的 Controller
,须要的自行来判断,像下面这段代码同样:
if(mappedHandler.getHandler() instanceof MultiActionController){
((MultiActionController)mappedHandler.getHandler()).xxx
}else if(mappedHandler.getHandler() instanceof XXX){
...
}else if(...){
...
}
这样假设若是咱们增长一个 HardController
,就要在代码中加入一行 if(mappedHandler.getHandler() instanceof HardController)
,这种形式就使得程序难以维护,也违反了设计模式中的开闭原则 – 对扩展开放,对修改关闭。
咱们来看看源码,首先是适配器接口 HandlerAdapter
public interface HandlerAdapter {
boolean supports(Object var1);
ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;
long getLastModified(HttpServletRequest var1, Object var2);
}
现该接口的适配器每个 Controller
都有一个适配器与之对应,这样的话,每自定义一个 Controller
须要定义一个实现 HandlerAdapter
的适配器。
springmvc 中提供的 Controller
实现类有以下

springmvc 中提供的 HandlerAdapter
实现类以下

HttpRequestHandlerAdapter
这个适配器代码以下
public class HttpRequestHandlerAdapter implements HandlerAdapter {
public HttpRequestHandlerAdapter() {
}
public boolean supports(Object handler) {
return handler instanceof HttpRequestHandler;
}
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
((HttpRequestHandler)handler).handleRequest(request, response);
return null;
}
public long getLastModified(HttpServletRequest request, Object handler) {
return handler instanceof LastModified ? ((LastModified)handler).getLastModified(request) : -1L;
}
}
当Spring容器启动后,会将全部定义好的适配器对象存放在一个List集合中,当一个请求来临时,DispatcherServlet
会经过 handler
的类型找到对应适配器,并将该适配器对象返回给用户,而后就能够统一经过适配器的 hanle()
方法来调用 Controller
中的用于处理请求的方法。
public class DispatcherServlet extends FrameworkServlet {
private List<HandlerAdapter> handlerAdapters;
//初始化handlerAdapters
private void initHandlerAdapters(ApplicationContext context) {
//..省略...
}
// 遍历全部的 HandlerAdapters,经过 supports 判断找到匹配的适配器
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
}
// 分发请求,请求须要找到匹配的适配器来处理
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
// 肯定当前请求的匹配的适配器.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
ha.getLastModified(request, mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
// ...省略...
}
经过适配器模式咱们将全部的 controller
统一交给 HandlerAdapter
处理,免去了写大量的 if-else
语句对 Controller
进行判断,也更利于扩展新的 Controller
类型。
参考:
刘伟:设计模式Java版
慕课网java设计模式精讲 Debug 方式+内存分析
孤落:Spring MVC中的适配器模式
ToughMind_:深刻浅出设计模式(五):7.适配器模式
推荐阅读
设计模式 | 简单工厂模式及典型应用
设计模式 | 工厂方法模式及典型应用
设计模式 | 抽象工厂模式及典型应用
设计模式 | 建造者模式及典型应用
设计模式 | 原型模式及典型应用
设计模式 | 外观模式及典型应用
设计模式 | 装饰者模式及典型应用
点击[阅读原文]可访问个人我的博客:http://laijianfeng.org

本文分享自微信公众号 - 小旋锋(whirlysBigData)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。