设计模式之适配器模式
定义
将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
在适配器模式中,我们通过增加一个新的适配器类来解决接口不兼容的问题,使得原本没有任何关系的类可以协同工作。
根据适配器类与适配者类的关系不同,适配器模式可分为对象适配器和类适配器两种,在对象适配器模式中,适配器与适配者之间是关联关系;在类适配器模式中,适配器与适配者之间是继承(或实现)关系。
类型
结构型
分类
具体又可以划分为类适配器、对象适配器。
角色
- 目标抽象类:Target,该角色把其他类转换为我们期望的接口,可以是一个抽象类或接口,也可以是具体类。
- 被适配者: Adaptee ,原有的接口,也是希望被适配的接口。
- 适配器: Adapter, 将被适配者和目标抽象类组合到一起的类。
缺省适配器模式(Default Adapter Pattern):当不需要实现一个接口所提供的所有方法时,可先设计一个抽象类实现该接口,并为接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可以选择性地覆盖父类的某些方法来实现需求,它适用于不想使用一个接口中的所有方法的情况,又称为单接口适配器模式。缺省适配器模式是适配器模式的一种变体,其应用也较为广泛。在JDK类库的事件处理包java.awt.event中广泛使用了缺省适配器模式,如WindowAdapter、KeyAdapter、MouseAdapter等。
类适配器和对象适配器
类适配器
被适配类:Adaptee
public class Adaptee {
public void adapterRequest(){
System.out.println("被适配者的方法");
}
}
目标抽象:Target接口
public interface Target {
void request();
}
怎么才可以在目标接口中的 request() 调用 Adaptee 的 adapteeRequest() 方法呢?
如果直接实现 Target 是不行的,还是调用不到被适配类的方法
ConcreteTarget类,具体目标类,实现了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.adapterRequest();
}
}
Adapter类继承了Adaptee类,所以称为类适配器(通过继承来实现)
应用层代码:
public class Test {
public static void main(String[] args) {
Target target = new ConcreteTarget();
target.request();
Target adapterTarget = new Adapter();
adapterTarget.request();
}
}
输出
类适配器的大致结构图如下所示,目标接口定义规范,适配器类相当于多了一层包装,可以调用到被适配者的方法。但是具体去实现一个目标接口的实现类是不能接触到被适配者类的属性和方法的。这样我们即可在新接口 Target 中适配旧的接口或类,然后供别人使用。
也就是说:我打个比方,有的人喜欢看电子档书籍,有的人喜欢看纸质档。
但是已有的就只是电子档,所以我们要增加一个步骤(在这里就是增加一个新接口),把电子档的书籍打印成纸质书籍给别人使用。
对象适配器
public class Adaptee {
public void adapterRequest(){
System.out.println("被适配者的方法");
}
}
public interface Target {
void request();
}
public class ConcreteTarget implements Target {
public void request() {
System.out.println("concreteTarget目标方法");
}
}
前面的这些类和类适配器中的一样。
对象适配器与类适配器不同之处在于,类适配器通过继承来完成适配,对象适配器则是通过关联来完成,这里稍微修改一下 Adapter 类即可将转变为对象适配器,把被适配者Adaptee关联进入适配器类Adapter
public class Adapter implements Target {
private Adaptee adaptee = new Adaptee();
public void request() {
adaptee.adapterRequest();
}
}
测试:
public class Test {
public static void main(String[] args) {
Target target = new ConcreteTarget();
target.request();
Target adapterTarget = new Adapter();
adapterTarget.request();
}
}
输出
结果一样,只是Adapter类与Adaptee类的关系不同而已(一个是继承,一个是组合)。(Adapter类与Adaptee类在这里是组合关系,两个生命周期一致)
示例
我们平时用充电器给手机充电,充电器可能需要将220v的电压转换成5v的电压,我们才能给手机正常充电。
AC220类(Adaptee),被适配者类,原有220v的电压。
public class AC220 {
public int outputAC220V(){
int output = 220;
System.out.println("输出交流电"+output+"V");
return output;
}
}
DC5类(Target接口),目标接口,需要转换成5v的电压。
public interface DC5 {
int outputDC5V();
}
PowerAdapter类(Adapter类),适配器类,将220v的电压转换成5v的电压。
public class PowerAdapter implements DC5 {
private AC220 ac220 = new AC220();
public int outputDC5V() {
int adapterInput = ac220.outputAC220V();
//变压器...
int adapterOutput = adapterInput/44;
System.out.println("使用PowerAdapter输入AC"+adapterInput+"V"+"输出DC"+adapterOutput+"V");
return adapterInput;
}
}
应用层代码:
public class Test {
public static void main(String[] args) {
DC5 dc5 = new PowerAdapter();
dc5.outputDC5V();
}
}
输出:
类图如下
具体应用
JDK代码中的适配器模式
StringReader
里面组合了String 实例,来给别人调用
import java.io.IOException;
import java.io.Reader;
public class StringReader extends Reader {
private String str;
private int length;
private int next = 0;
private int mark = 0;
public StringReader(String s) {
this.str = s;
this.length = s.length();
}
//确认该流没有被关闭
private void ensureOpen() throws IOException {
if (str == null)
throw new IOException("Stream closed");
}
// 读取字符
public int read() throws IOException {
synchronized (lock) {
ensureOpen();
if (next >= length)
return -1;
return str.charAt(next++);
}
}
// 读取字符数组中的字符
public int read(char cbuf[], int off, int len) throws IOException {
synchronized (lock) {
ensureOpen();
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
((off + len) > cbuf.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
if (next >= length)
return -1;
int n = Math.min(length - next, len);
str.getChars(next, next + n, cbuf, off);
next += n;
return n;
}
}
// 跳转
public long skip(long ns) throws IOException {
synchronized (lock) {
ensureOpen();
if (next >= length)
return 0;
// Bound skip by beginning and end of the source
long n = Math.min(length - next, ns);
n = Math.max(-next, n);
next += n;
return n;
}
}
// 确认该字符是否已被读取
public boolean ready() throws IOException {
synchronized (lock) {
ensureOpen();
return true;
}
}
// 是否支持mark操作
public boolean markSupported() {
return true;
}
//标识当前位置
public void mark(int readAheadLimit) throws IOException {
if (readAheadLimit < 0){
throw new IllegalArgumentException("Read-ahead limit < 0");
}
synchronized (lock) {
ensureOpen();
mark = next;
}
}
// 重置当前位置
public void reset() throws IOException {
synchronized (lock) {
ensureOpen();
next = mark;
}
}
// 关闭流
public void close() {
str = null;
}
}
StringWriter
里面组合了StringBuffer,来给别人使用
import java.io.IOException;
import java.io.Writer;
public class StringWriter extends Writer {
private StringBuffer buf;
public StringWriter() {
buf = new StringBuffer();
lock = buf;
}
public StringWriter(int initialSize) {
if (initialSize < 0) {
throw new IllegalArgumentException("Negative buffer size");
}
buf = new StringBuffer(initialSize);
lock = buf;
}
// 写入字符
public void write(int c) {
buf.append((char) c);
}
// 写入数组中的字符
public void write(char cbuf[], int off, int len) {
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
((off + len) > cbuf.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
buf.append(cbuf, off, len);
}
//写入字符串
public void write(String str) {
buf.append(str);
}
// 在指定位置写入字符串
public void write(String str, int off, int len) {
buf.append(str.substring(off, off + len));
}
// 增加字符串序列
public StringWriter append(CharSequence csq) {
if (csq == null)
write("null");
else
write(csq.toString());
return this;
}
// 在指定位置增加字符串序列
public StringWriter append(CharSequence csq, int start, int end) {
CharSequence cs = (csq == null ? "null" : csq);
write(cs.subSequence(start, end).toString());
return this;
}
// 增加字符
public StringWriter append(char c) {
write(c);
return this;
}
public String toString() {
return buf.toString();
}
public StringBuffer getBuffer() {
return buf;
}
public void flush() {
}
public void close() throws IOException {
}
}
spring AOP中的适配器模式
在Spring的Aop中,使用的 Advice(通知) 来增强被代理类的功能。
Advice的类型有:MethodBeforeAdvice、AfterReturningAdvice、ThrowsAdvice
在每个类型 Advice 都有对应的拦截器,MethodBeforeAdviceInterceptor、AfterReturningAdviceInterceptor、ThrowsAdviceInterceptor
Spring需要将每个 Advice 都封装成对应的拦截器类型,返回给容器,所以需要使用适配器模式对 Advice 进行转换
三个被适配者 如下:
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);
}
三个适配器类分别如下,注意其中的 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)
JpaVendorAdapter接口
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 类型。
JUnit中的适配器模式
通俗而言,就是要适配成TestCase类。这里重点看TestCase的runTest()方法:
protected void runTest() throws Throwable {
assertNotNull("TestCase.fName cannot be null", this.fName);
Method runMethod = null;
try {
//获取要测试的方法
runMethod = getClass().getMethod(this.fName, (Class[]) null);
}
catch (NoSuchMethodException e) {
fail("Method \"" + this.fName + "\" not found");
}
//判断要测试的方法是否为公用方法
if (!Modifier.isPublic(runMethod.getModifiers())) {
fail("Method \"" + this.fName + "\" should be public");
}
//java反射机制
try {
runMethod.invoke(this, new Object[0]);
}
catch (InvocationTargetException e) {
e.fillInStackTrace();
throw e.getTargetException();
}
catch (IllegalAccessException e) {
e.fillInStackTrace();
throw e;
}
}
总结
主要优点
- 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构。
- 增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了适配者类的复用性,同一个适配者类可以在多个不同的系统中复用。
- 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,符合“开闭原则”。
主要缺点
- 会让系统看起来很复杂,不易维护(因为需要从全局考虑)
- 增加代码的阅读难度
适配器模式最好在详细设计不要考虑它,它不是为了解决还处在开发阶段的问题,而是解决正在服役的项目问题。
适用场景
想要使用一个已经存在的类,但是它却不符合现有的接口规范,导致无法直接去访问,这时创建一个适配器就能间接去访问这个类中的方法。
我们有一个类,想将其设计为可重用的类,我们可以创建适配器,将这个类来适配其他没有提供合适接口的类。
优点
和其他模式的对比
- 适配器模式和装饰者模式:装饰者模式是对以前的类进行进一步的封装与增强,目的是一层一层的增加功能,而适配器模式主要是为了协调多个调用者不同的调用方式而设计出来的。
- 适配器模式和外观模式:都是对现有类的封装,外观模式定义了新的接口,适配器则是复用已有的接口,适配器是两个接口协调工作,而外观模式则是为了别人使用的方便,提供了一个比较易用的接口
References:
- https://whirlys.blog.csdn.net/article/details/82780560
- https://blog.csdn.net/lu__peng/article/details/79117894
- https://blog.csdn.net/liuquan0071/article/details/50506121
这些东西其他博客都总结的非常好,我这里只是做了一点点修改与扩充,以后有了不同的理解我再进行补充完善。
(写博客主要是对自己学习的归纳整理,资料大部分来源于书籍、网络资料和自己的实践,整理不易,但是难免有不足之处,如有错误,请大家评论区批评指正。同时感谢广大博主和广大作者辛苦整理出来的资源和分享的知识。)
更多推荐
所有评论(0)