适配器模式
适配器模式(Adapter Pattern):将一个接口转换成客户希望的另一个接口, 使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。 适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
在适配器模式中,我们通过增加一个新的适配器类来解决接口不兼容的问题 ,使得原本没有任何关系的类可以协同工作。
根据适配器类与适配者类的关系不同,适配器模式可分为对象适配器和类适配器两种,
- 在对象适配器模式中,适配器与适配者之间是关联关系;
- 在类适配器模式中,适配器与适配者之间是继承(或实现)关系。
优缺点
将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构。 增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的, 而且提高了适配者的复用性,同一个适配者类可以在多个不同的系统中复用。 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器, 也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。
类适配器模式还有如下优点
由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。
对象适配器模式还有如下优点:
一个对象适配器可以把多个不同的适配者适配到同一个目标; 可以适配一个适配者的子类,由于适配器和适配者之间是关联关系, 根据“里氏代换原则”,适配者的子类也可通过该适配器进行适配。
类适配器模式的缺点如下:
对于Java、C#等不支持多重类继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适配者; 适配者类不能为最终类,如在Java中不能为final类,C#中不能为sealed类; 在Java、C#等语言中,类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性。
对象适配器模式的缺点
与类适配器模式相比,要在适配器中置换适配者类的某些方法比较麻烦。 如果一定要置换掉适配者类的一个或多个方法,可以先做一个适配者类的子类, 将适配者类的方法置换掉,然后再把适配者类的子类当做真正的适配者进行适配,实现过程较为复杂。
适用场景
系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有这些类的源代码。 想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。
心得
尽量使用对象适配器的实现方式,多用组合、少用继承。
代码展示
对象适配器1
package designpatterns.adapter.object.zyr;
public class Banner {
private String name;
public Banner(String name){
this.name=name;
}
public void showWithParen(){
System.out.println("("+name+")");
}
public void showWithAster(){
System.out.println("*"+name+"*");
}
}
package designpatterns.adapter.object.zyr;
public abstract class Print {
public abstract void printWeak();
public abstract void printStrong();
}
package designpatterns.adapter.object.zyr;
/**
* 可以看到Main函数、Banner类都没有改动,将Print接口变成抽象类,那么PrintBanner不能同时继承两个类,
* 因此将Banner对象组合到适配器之中,因此叫做对象适配器,这样也可以实现预期的结果。
* 两者的区别也是非常明显的,最好推荐使用前者,或者根据实际情况需要进行甄别。
*
*/
public class PrintBanner extends Print {
Banner banner;
public PrintBanner(String name) {
banner=new Banner(name);
}
public void printWeak() {
System.out.println("...开始弱适配...");
banner.showWithParen();
System.out.println("...弱适配成功...");
System.out.println();
}
public void printStrong() {
System.out.println("...开始强适配...");
banner.showWithAster();
System.out.println("...强适配成功...");
System.out.println();
}
}
package designpatterns.adapter.object.zyr;
public class PrintBannerMain {
public static void main(String[] args) {
Print p=new PrintBanner("Fire");
p.printStrong();
p.printWeak();
}
}
对象适配器2
package designpatterns.adapter.object.ac;
public interface AC {
int outputAC();
}
package designpatterns.adapter.object.ac;
public class AC110 implements AC {
public final int output = 110;
public int outputAC() {
return 110;
}
}
package designpatterns.adapter.object.ac;
public class AC220 implements AC {
public final int output = 220;
public int outputAC() {
return output;
}
}
package designpatterns.adapter.object.ac;
/**
* 适配器接口
*
*/
public interface DC5Adapter {
//用于检查输入的电压是否与适配器匹配,
boolean support(AC ac);
//用于将输入的电压变换为 5V 后输出
int outputDC5V(AC ac);
}
package designpatterns.adapter.object.ac;
public class ChinaPowerAdapter implements DC5Adapter {
public static final int voltage = 220;
public boolean support(AC ac) {
return (voltage == ac.outputAC());
}
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;
}
}
package designpatterns.adapter.object.ac;
public class JapanPowerAdapter implements DC5Adapter {
public static final int voltage = 110;
public boolean support(AC ac) {
return (voltage == ac.outputAC());
}
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;
}
}
package designpatterns.adapter.object.ac;
import java.util.LinkedList;
import java.util.List;
public class ACMain {
private List<DC5Adapter> adapters = new LinkedList<DC5Adapter>();
ACMain() {
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) {
ACMain test = new ACMain();
AC chinaAC = new AC220();
DC5Adapter adapter = test.getPowerAdapter(chinaAC);
adapter.outputDC5V(chinaAC);
// 去日本旅游,电压是 110V
AC japanAC = new AC110();
adapter = test.getPowerAdapter(japanAC);
adapter.outputDC5V(japanAC);
}
}
类适配器1
package designpatterns.adapter.clazz.example;
/**
* 定义一个目标接口
*/
public interface Target {
void request();
}
package designpatterns.adapter.clazz.example;
/**
* 一个将被适配的类
*/
public class Adaptee {
public void adapteeRequest() {
System.out.println("被适配者的方法");
}
}
package designpatterns.adapter.clazz.example;
/**
* 一种错误的实现方式
*
* 怎么才可以在目标接口中的 request() 调用 Adaptee 的 adapteeRequest() 方法呢?
*
* 如果直接实现 Target 是不行的
*
*/
public class ConcreteTarget implements Target {
public void request() {
System.out.println("concreteTarget目标方法");
}
}
package designpatterns.adapter.clazz.example;
/**
* 一个正确的方式
*
* 如果通过一个适配器类,实现 Target 接口,同时继承了 Adaptee 类,然后在实现的 request() 方法中调用父类的 adapteeRequest() 即可实现
*
*/
public class Adapter extends Adaptee implements Target {
public void request() {
//...一些操作...
super.adapteeRequest();
//...一些操作...
}
}
package designpatterns.adapter.clazz.example;
public class AdapterMain {
public static void main(String[] args) {
Target target = new ConcreteTarget();
target.request();
Target adapterTarget = new Adapter();
adapterTarget.request();
}
}
类适配器2
package designpatterns.adapter.clazz.zyr;
public interface Print {
void printWeak();
void printStrong();
}
package designpatterns.adapter.clazz.zyr;
public class Banner {
private String name;
public Banner(String name){
this.name=name;
}
public void showWithParen(){
System.out.println("("+name+")");
}
public void showWithAster(){
System.out.println("*"+name+"*");
}
}
package designpatterns.adapter.clazz.zyr;
public class PrintBanner extends Banner implements Print {
public PrintBanner(String name) {
super(name);
}
public void printWeak() {
System.out.println("...开始弱适配...");
showWithParen();
System.out.println("...弱适配成功...");
System.out.println();
}
public void printStrong() {
System.out.println("...开始强适配...");
showWithAster();
System.out.println("...强适配成功...");
System.out.println();
}
}
package designpatterns.adapter.clazz.zyr;
public class PrintBannerMain {
public static void main(String[] args) {
Print p = new PrintBanner("Fire");
p.printStrong();
p.printWeak();
}
}
源码分析适配器模式的典型应用
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;
}
}
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 实现类有如下