Java设计模式及应用场景之《适配器模式》

一、适配器模式定义

将一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。

二、适配器模式的结构和说明

1、对象适配器

在这里插入图片描述
2、类适配器
在这里插入图片描述

  • Target 目标角色
    该角色定义把其他类转换为何种接口,也就是我们的期望接口。
  • Adaptee 源角色
    已经存在的接口,通常能满足客户端的功能需求,但是接口与客户要求的不一致,需要被适配。
  • Adapter 适配器角色
    通过继承或者类关联的方式,把Adaptee适配成Client需要的Target。
三、类适配器与对象适配器的区别

  类适配器使用对象继承的方式,对象适配器是对象的组合关系,也可以说是对象的关联关系,这是两者的根本区别。(实际项目中对象适配器使用到的场景相对较多。)

四、适配器模式示例–Mybatis日志模块

  我们以Mybatis日志模块为例,来介绍一下适配器模式的应用。
  Mybatis自己本身不提供日志功能,而是依赖其它日志框架来实现,如log4j,log4j2,commons-logging,slf4j等等。
  由于各个厂商提供的日志api、日志级别等都不尽相同,所以mybatis对所有支持的日志框架使用对象适配器模式做了封装。

org.apache.ibatis.logging.Log (Target 目标角色 )

/**
 * Mybatis定义的统一日志接口
 */
public interface Log {

    boolean isDebugEnabled();

    boolean isTraceEnabled();

    void error(String s, Throwable e);

    void error(String s);

    void debug(String s);

    void trace(String s);

    void warn(String s);

}

org.apache.ibatis.logging.log4j.Log4jImpl (Adapter 适配器角色)

/**
 * 针对log4j的适配器,实现了Log接口,并封装了 org.apache.log4j.Logger对象
 */
public class Log4jImpl implements Log {

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

    /**
     * Adaptee 源角色
     * org.apache.log4j日志组件对象
     */
    private final 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);
    }

}

  以上,针对log4j的适配器已经完成,其它日志组件的适配器基本相似。Mybatis在LogFactory的静态方法里按照特定的顺序,寻找并加载第三方日志组件,获取日志适配器对象时,只需要调用 getLog(Class<?> clazz) 方法即可。

/**
 * 获取Log接口具体实现的工厂
 */
public final class LogFactory {

    public static final String MARKER = "MYBATIS";

    /**
     * 记录当前使用的第三方日志框架的适配器的构造方法
     */
    private static Constructor<? extends Log> logConstructor;

    /**
     * 尝试加载第三方日志组件适配器
     * 第三方日志组件加载优先级如下:slf4J→commonsLoging→Log4J2→Log4J→JdkLog
     */
    static {
        tryImplementation(LogFactory::useSlf4jLogging);
        tryImplementation(LogFactory::useCommonsLogging);
        tryImplementation(LogFactory::useLog4J2Logging);
        tryImplementation(LogFactory::useLog4JLogging);
        tryImplementation(LogFactory::useJdkLogging);
        tryImplementation(LogFactory::useNoLogging);
    }

    private LogFactory() {
        // disable construction
    }

    /**
     * 获取当前使用的第三方日志框架的适配器对象
     */
    public static Log getLog(Class<?> clazz) {
        return getLog(clazz.getName());
    }

    public static Log getLog(String logger) {
        try {
            return logConstructor.newInstance(logger);
        } catch (Throwable t) {
            throw new LogException("Error creating logger for logger " + logger + ".  Cause: " + t, t);
        }
    }

    public static synchronized void useCustomLogging(Class<? extends Log> clazz) {
        setImplementation(clazz);
    }

    public static synchronized void useSlf4jLogging() {
        setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class);
    }

    public static synchronized void useCommonsLogging() {
        setImplementation(org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl.class);
    }

    public static synchronized void useLog4JLogging() {
        setImplementation(org.apache.ibatis.logging.log4j.Log4jImpl.class);
    }

    public static synchronized void useLog4J2Logging() {
        setImplementation(org.apache.ibatis.logging.log4j2.Log4j2Impl.class);
    }

    public static synchronized void useJdkLogging() {
        setImplementation(org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl.class);
    }

    public static synchronized void useStdOutLogging() {
        setImplementation(org.apache.ibatis.logging.stdout.StdOutImpl.class);
    }

    public static synchronized void useNoLogging() {
        setImplementation(org.apache.ibatis.logging.nologging.NoLoggingImpl.class);
    }

    /**
     * 尝试加载日志框架
     */
    private static void tryImplementation(Runnable runnable) {
        if (logConstructor == null) {
            try {
                runnable.run();
            } catch (Throwable t) {
                // ignore
            }
        }
    }

    private static void setImplementation(Class<? extends Log> implClass) {
        try {
            // 获取指定适配器的构造方法
            Constructor<? extends Log> candidate = implClass.getConstructor(String.class);
            // 实例化适配器
            Log log = candidate.newInstance(LogFactory.class.getName());
            if (log.isDebugEnabled()) {
                log.debug("Logging initialized using '" + implClass + "' adapter.");
            }
            // 记录第三方日志组件适配器的构造方法,用于之后获取日志对象
            logConstructor = candidate;
        } catch (Throwable t) {
            throw new LogException("Error setting Log implementation.  Cause: " + t, t);
        }
    }

}
五、适配器模式的优缺点

优点

  • 更好的复用性
    如果功能已经存在,只是接口不兼容,通过适配器模式就可以让这些功能得到更好的复用。
  • 更好的扩展性
    在实现适配器功能时,可以调用自己开发的功能,从而自然的扩展系统的功能。

缺点

  • 过多的适配器类,会让系统变得凌乱,不容易进行整体把握。
    如我们调用的是A接口,内部确被适配成了B接口。如果系统内出现过多这种情况,就会变得非常凌乱。
六、适配器模式的应用场景
  • 系统需要使用现有已投产的类,而这些类的接口不符合系统的需要。
  • 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。
  • Java中的数据库连接工具JDBC,JDBC定义一个客户端通用的抽象接口,每一个具体数据库引擎(如SQL Server、Oracle、MySQL等)的JDBC驱动软件都是一个介于JDBC接口和数据库引擎接口之间的适配器软件。
  • 例子中的Mybatis日志模块。
  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 14
    评论
Java常用的设计模式包括: 1. 单例模式(Singleton Pattern):用于确保一个类只有一个实例,并提供全局访问点。 应用场景:多线程环境下的资源共享、数据库连接池等。 2. 工厂模式(Factory Pattern):通过一个工厂类来创建对象,将对象的创建和使用分离。 应用场景:对象的创建比较复杂,需要隐藏具体实现逻辑的时候。 3. 抽象工厂模式(Abstract Factory Pattern):提供一个接口用于创建相关或依赖对象的家族,而不需要明确指定具体类。 应用场景:需要创建一系列相互关联或依赖的对象时。 4. 建造者模式(Builder Pattern):通过一个指导者来按照一定的步骤创建复杂对象,将对象的构造和表示分离。 应用场景:需要创建一个包含多个组成部分的复杂对象,并且需要控制创建过程的时候。 5. 原型模式(Prototype Pattern):通过复制现有对象来创建新对象,避免了直接使用new关键字创建对象。 应用场景:需要创建大量相似对象,并且创建过程比较耗时或复杂的时候。 6. 适配器模式(Adapter Pattern):将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的类可以一起工作。 应用场景:需要使用一个已有的类,但其接口与需要的接口不一致时。 7. 装饰器模式(Decorator Pattern):动态地将责任附加到对象上,在保持功能完整性的同时,灵活地给对象添加新的行为。 应用场景:需要动态地给一个对象添加功能,或者为一个对象添加多个不同的功能。 8. 观察者模式(Observer Pattern):定义一种一对多的依赖关系,当一个对象状态发生改变时,所有依赖它的对象都会收到通知并自动更新。 应用场景:当一个对象的改变需要同时改变其他对象的时候。 9. 策略模式(Strategy Pattern):定义一系列算法,并将每个算法封装起来,使它们可以互相替换,使得算法可以独立于使用它的客户而变化。 应用场景:需要在多个算法选择一种合适的算法时。 10. 模板方法模式(Template Method Pattern):定义一个操作的算法骨架,将一些步骤延迟到子类实现。 应用场景:当算法有固定的骨架,但其的某些步骤可以有多种实现方式时。 以上是Java常用的设计模式及其应用场景,根据具体情况选择合适的设计模式可以提高代码的可维护性、扩展性和复用性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晓呆同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值