当前位置:首页 > 服务端 > 设计模式之适配器模式

设计模式之适配器模式

2022年11月08日 19:16:14服务端6

定义

将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(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();
    }
}

输出
设计模式之适配器模式 _ JavaClub全栈架构师技术笔记
类适配器的大致结构图如下所示,目标接口定义规范,适配器类相当于多了一层包装,可以调用到被适配者的方法。但是具体去实现一个目标接口的实现类是不能接触到被适配者类的属性和方法的。这样我们即可在新接口 Target 中适配旧的接口或类,然后供别人使用。
也就是说:我打个比方,有的人喜欢看电子档书籍,有的人喜欢看纸质档。
但是已有的就只是电子档,所以我们要增加一个步骤(在这里就是增加一个新接口),把电子档的书籍打印成纸质书籍给别人使用。
设计模式之适配器模式 _ JavaClub全栈架构师技术笔记

对象适配器

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();
    }
}

输出
设计模式之适配器模式 _ JavaClub全栈架构师技术笔记
结果一样,只是Adapter类与Adaptee类的关系不同而已(一个是继承,一个是组合)。(Adapter类与Adaptee类在这里是组合关系,两个生命周期一致)
设计模式之适配器模式 _ JavaClub全栈架构师技术笔记

示例

我们平时用充电器给手机充电,充电器可能需要将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();
    }
}

输出:
设计模式之适配器模式 _ JavaClub全栈架构师技术笔记
类图如下
设计模式之适配器模式 _ JavaClub全栈架构师技术笔记

具体应用

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() 创建对应类型的拦截器
设计模式之适配器模式 _ JavaClub全栈架构师技术笔记
这里应该属于对象适配器模式,关键字 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 实现类有如下
设计模式之适配器模式 _ JavaClub全栈架构师技术笔记

springmvc 中提供的 HandlerAdapter 实现类如下
设计模式之适配器模式 _ JavaClub全栈架构师技术笔记
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

这些东西其他博客都总结的非常好,我这里只是做了一点点修改与扩充,以后有了不同的理解我再进行补充完善。

(写博客主要是对自己学习的归纳整理,资料大部分来源于书籍、网络资料和自己的实践,整理不易,但是难免有不足之处,如有错误,请大家评论区批评指正。同时感谢广大博主和广大作者辛苦整理出来的资源和分享的知识。)

作者:codingXT
来源链接:https://blog.csdn.net/qq_37774171/article/details/122643900

版权声明:
1、JavaClub(https://www.javaclub.cn)以学习交流为目的,由作者投稿、网友推荐和小编整理收藏优秀的IT技术及相关内容,包括但不限于文字、图片、音频、视频、软件、程序等,其均来自互联网,本站不享有版权,版权归原作者所有。

2、本站提供的内容仅用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯相关权利人及本网站的合法权利。
3、本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站(javaclubcn@163.com),我们将第一时间核实后及时予以删除。


本文链接:https://www.javaclub.cn/server/68600.html

标签: 设计模式
分享给朋友:

“设计模式之适配器模式” 的相关文章

什么是设计模式?详解设计模式概念及几大原则

什么是设计模式?详解设计模式概念及几大原则

​ hello,各位小伙伴大家好,又到了一篇一次的斗图环节,天气冷了,各位注意保暖。 今天我们来讲讲设计模式,在我们学习Java的时候,时常听到单例模式,多例模式,还有使用Spring的时候,默认采用的单例模式,你所听到的“饱汉式”、“饿汉式”,都是对设计模式的形容。那么什么是设计模式呢?...

设计模式系列(一)—简单工厂模式

设计模式系列(一)—简单工厂模式

​ hello各位程序员大哥大姐好,上期我讲解了设计模式的概念及分类,学习了UML类图的认识,今天我们就趁热打铁,学习一下简单工厂模式,以后每一个模式会出一篇文章,供各位及我自己查阅学习。斗图还是不能忘的,为了苟且的生活,大家加油,生活不止眼前的苟且,还有远方的苟且。 第一节:简单工...

常用设计模式系列(六)—单例模式

常用设计模式系列(六)—单例模式

一、前言各位大佬好,由于本人的原因,拖更了几天休息了一下,吃了点好吃的,为了填饱自己的肚子,逛遍了天下美食(我所在这个城市的某个角落),所谓干饭人干饭魂,干饭人吃饭得用盆。吃饱喝足之后,活还是要干的,所以今天继续开始我们的设计模式,今天讲解设计模式之“单例模式”,单例模式算的上是在整个设计模式体系中...

关于设计模式

掌握设计模式并不是件很难的事情,关键在于多思考,多实践,不要听到人家说懂几个设计模式就很“牛”,只要用心学习,设计模式也就那么回事,你也可以很“牛”的,一定要有信心。 在学习每一个设计模式时至少应该掌握如下几点:这个设计模式的意图是什么,它...

炒冷饭系列:设计模式 单例模式

炒冷饭系列:设计模式 单例模式

炒冷饭系列:设计模式 单例模式 摘要: 原创出处: http://www.cnblogs.com/Alandre/ 泥沙砖瓦浆木匠 希望转载,保留摘要,谢谢! 亲爱我,孝何难;亲恶我,孝方贤。 一、什么是单例模式...

java多线程12种设计模式

1、Single Threaded Execution Pattern(单线程执行模式) 2、Immutable Pattern(不可变模式) 3、Guarded Suspension Pattern(防卫暂停模式) 4、Balking Pattern(止步模式...

Java中几种常用的设计模式

在Java的学习中比较常见的几种设计模式 尚学堂-马士兵老师大致讲的,总结一下 1.单例模式(单态模式): 在程序运行过程中只有几个类或是只有一个类,达到“单例模式”。无论外部类调用多少次“构造”,内存中只是分配一次实例化...

《java编程思想》:设计模式(不定期更新)

1.策略设计模式   创建一个能够根据所传递的参数对象的不同而具有不同的方法,被称为策略设计模式。这类方法包含索要执行的算法中固定不变的部分,而“策略”包含变化的部分。策略就是传递进去的参数对象。在下面的代码示例中,Process对象就是策略。应用在了s上。 代码示例:...

Java 高并发第二阶段实战---高并发设计模式,内存模型,CPU一致性协议,volatile关键字剖析

     第二阶段的课程主要围绕着Volatile关键字,内存重排序,Happen-Before,Cpu一致性协议,高并发下的设计模式以及类加载器几个大的方面展开,下面是内容详细信息,本教程是本人录制,下载地址为 高并发设计模式 汪文...

2021Java高级面试题!java设计模式场景

2021Java高级面试题!java设计模式场景

一、掀起Spring的盖头来 Spring框架的由来 Spring框架概述 二、Spring的IoC容器 重头开始认识loC的基本概念:(构造方法注入+scttcr方法注入+接口注入) 运筹帷幄的秘密...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。