java 设计模式 - 适配器模式

本贴最后更新于 2500 天前,其中的信息可能已经天翻地覆

适配器模式简介

将一个类的接口转换为另一个类的接口。适配器模式使得原本由于接口不兼容而不能 在一起工作的类可以在一起工作。

适配器模式的本质:转换匹配,复用功能。

适配器模式结构和说明

适配器模式类图

Client:客户端,调用自己需要的领域接口 Target。
Target:定义客户端需要的跟特定领域相关的接口。
Adaptee:已经存在的接口,通常能满足客户端的功能要求,但是接口与客户端要求的特定领域接口不一致,需要被适配
Adapter:适配器,把 Adaptee 适配成为 Client 需要的 Target。

适配器模式例举

模式示例代码

  • 先看看 Target 接口的定义,示例代码如下:

    /**
     * 定义客户端使用的接口,与特定领域相关
     */
    public interface Target {
        /**
         * 示意方法,客户端请求处理的方法
         */
        public void request();
    }
    
  • 再看看需要被适配的对象定义,示例代码如下:

    /**
     * 已经存在的接口,这个接口需要被适配
     */
    public class Adaptee {
        /**
         * 示意方法,原本已经存在,已经实现的方法
         */
        public void specificRequest() {
            //具体的功能处理
        }
    }
    
  • 再看看适配器对象的基本实现,示例代码如下:

    /**
     * 适配器
     */
    public class Adapter implements Target {
        /**
         * 持有需要被适配的接口对象
         */
        private Adaptee adaptee;
    
        /**
         * 构造方法,传入需要被适配的对象
         *
         * @param adaptee 需要被适配的对象
         */
        public Adapter(Adaptee adaptee) {
            this.adaptee = adaptee;
        }
    
        public void request() {
            //可能转调已经实现了的方法,进行适配
            adaptee.specificRequest();
        }
    }
    
  • 再来看看使用适配器的客户端,示例代码如下:

    /**
     * 使用适配器的客户端
     */
    public class Client {
        public static void main(String[] args) {
            // 创建需被适配的对象
            Adaptee adaptee = new Adaptee();
            // 创建客户端需要调用的接口对象
            Target target = new Adapter(adaptee);
            // 请求处理
            target.request();
        }
    }
    

模式举例

这里还是以 Mybatis 源码包 org.apache.ibatis.logging 举例。其中类 org.apache.ibatis.logging.log4j.Log4jImpl 源码如下:

/**
 * @author Eduardo Macarron
 */
public class Log4jImpl implements Log {

  private static final String FQCN = Log4jImpl.class.getName();

  private Logger log;

  public Log4jImpl(String clazz) { log = Logger.getLogger(clazz); }

  @Override
  public boolean isDebugEnabled() { return log.isDebugEnabled(); }

  @Override
  public boolean isTraceEnabled() { return log.isTraceEnabled(); }

  @Override
  public void error(String s, Throwable e) { log.log(FQCN, Level.ERROR, s, e); }

  @Override
  public void error(String s) { log.log(FQCN, Level.ERROR, s, null); }

  @Override
  public void debug(String s) { log.log(FQCN, Level.DEBUG, s, null); }

  @Override
  public void trace(String s) { log.log(FQCN, Level.TRACE, s, null); }

  @Override
  public void warn(String s) { log.log(FQCN, Level.WARN, s, null); }

}

另外还有几个重要的类,通过类图如下:

mybatis 适配器

分析源码可知道, Log 为 mybatis 日志接口,而 Logger 为 log4j 日志接口。mybatis 为兼容 log4j 日志,于适配器 Log4jImpl 中进行了接口方法的转换,通过调用 Target Log 接口实现日志记录功能。

当然,这里只是从单适配器的角度分析的。从横向来看,mybatis 同时为 Common Logging Slf4j Jdk Log 等分别添加了不同 Adapter,再通过 LogFactory 选择具体可用的 Log 实现。

适配器的应用远不于此。还有比如 SpringMVC 中 DispatcherServlet 在处理不同类型的 Controller 时,通过 HandlerAdapter 来调用不同的具体 Handler、Jdk 中提供标准的 jdbc 接口,通过不同数据库驱动实现不同数据库连接等等。

适配器模式讲解

  1. 模式的功能

    适配器模式的主要功能是进行转换匹配,目的是复用已有的功能,而不是来实现新的接口。也就是说,客户端需要的功能应该是已经实现好了的,不需要适配器模式来实现,适配器模式主要负责把不兼容的接口转换成客户端期望的样子就好了

    但这并不是说,在适配器里面就不能实现功能,适配器里面可以实现功能,称这种适配器为智能适配器。再说了,在接口匹配和转换的过程中,也是有可能需要额外实现一定的功能,才能够转换过来的,比如需要调整参数以进行匹配等。

  2. Adaptee 和 Target 的关系

    适配器模式中被适配的接口 Adaptee 和适配成为的接口 Target 是没有关联的,也就是说,Adaptee 和 Target 中的方法既可以相同,也可以不同,极端情况下两个接口里面的方法可能是完全不同的,当然极端情况下也可以完全相同。

    这里所说的相同和不同,是指的方法定义的名称、参数列表、返回值、包括方法本身的功能都可以相同和不同。

  3. 对象组合

    根据前面的实现,你会发现,适配器的实现方式其实是依靠对象组合的方式。通过给适配器对象组合被适配的对象,然后当客户端调用 Target 的时候,适配器会把相应的功能,委托给被适配的对象去完成。

思考适配器模式

  • 适配器模式的本质

    适配器模式的本质:转换匹配,复用功能。

    适配器通过转换调用已有的实现,从而能把已有的实现匹配成需要的接口,使之能满足客户端的需要。也就是说转换匹配是手段,而复用已有的功能才是目的。

    在进行转换匹配的过程中,适配器还可以在转换调用的前后实现一些功能处理,也就是实现智能的适配。

  • 何时选用适配器模式

    如果你想要使用一个已经存在的类,但是它的接口不符合你的需求,这种情况可以使用适配器模式,来把已有的实现转换成你需要的接口

    如果你想创建一个可以复用的类,这个类可能和一些不兼容的类一起工作,这种情况可以使用适配器模式,到时候需要什么就适配什么

    如果你想使用一些已经存在的子类,但是不可能对每一个子类都进行适配,这种情况可以选用对象适配器,直接适配这些子类的父类就可以了。

适配器模式的优缺点

  • 优点方面

    (1)更好的复用性

    (2)更好的可扩展性

  • 缺点方面

    (1)过多的适配器容易增加系统的复杂度

  • 设计模式

    设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

    200 引用 • 120 回帖
  • 适配器模式
    3 引用

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...