内容简介:更多内容可访问我的个人博客:laijianfeng.org关注【小旋锋】微信公众号,及时接收博文推送适配器模式(Adapter Pattern):将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
更多内容可访问我的个人博客:laijianfeng.org
关注【小旋锋】微信公众号,及时接收博文推送
适配器模式
适配器模式(Adapter Pattern):将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
在适配器模式中,我们通过增加一个新的适配器类来解决接口不兼容的问题,使得原本没有任何关系的类可以协同工作。
根据适配器类与适配者类的关系不同,适配器模式可分为对象适配器和类适配器两种,在 对象适配器模式 中,适配器与适配者之间是 关联 关系;在 类适配器模式 中,适配器与适配者之间是 继承 (或实现)关系。
角色
Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。
Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。
Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。
缺省适配器模式(Default Adapter Pattern):当不需要实现一个接口所提供的所有方法时,可先设计一个抽象类实现该接口,并为接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可以选择性地覆盖父类的某些方法来实现需求,它适用于不想使用一个接口中的所有方法的情况,又称为单接口适配器模式。缺省适配器模式是适配器模式的一种变体,其应用也较为广泛。在JDK类库的事件处理包java.awt.event中广泛使用了缺省适配器模式,如WindowAdapter、KeyAdapter、MouseAdapter等。
示例
类适配器
首先有一个已存在的将被适配的类
public class Adaptee { public void adapteeRequest() { System.out.println("被适配者的方法"); } } 复制代码
定义一个目标接口
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的一致性异常 ,目前支持如下数据库(DB2、DERBY、H2、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 方式+内存分析
ToughMind_: 深入浅出设计模式(五):7.适配器模式
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 设计模式——订阅模式(观察者模式)
- 设计模式-简单工厂、工厂方法模式、抽象工厂模式
- java23种设计模式-门面模式(外观模式)
- 设计模式-享元设计模式
- Java 设计模式之工厂方法模式与抽象工厂模式
- JAVA设计模式之模板方法模式和建造者模式
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
jQuery基础教程(第3版)
[美] Jonathan Chaffer、[美] Karl Swedberg / 李松峰 / 人民邮电出版社 / 2012-4 / 59.00元
内容简介: jQuery是功能强大却又简洁明快的轻量级JavaScript库,出自名家之手。在DOM操作、事件处理、Ajax特性以及动画特效等方面的出色表现,使它迅速从众多JavaScript库中脱颖而出,成为一颗闪亮的明星。 本书是《jQuery基础教程(第2版)》的升级版,涵盖了jQuery 1.6及1.7的各种新特性,内容源自著名jQuery资源网站LearningjQuery.......一起来看看 《jQuery基础教程(第3版)》 这本书的介绍吧!