适配器模式

需求

image-20210802101904301

欧洲插头协议与实现

创建 EuropePlugInterface.java 接口定义欧洲插头的插口协议

/**
 * @author BNTang
 */
public interface EuropePlugInterface {
    /**
     * 欧洲插头连接
     */
    void europeConnect();
}

然后在紧接着创建 EuropePlug.java 实现欧洲插头的协议

/**
 * @author BNTang
 */
public class EuropePlug implements EuropePlugInterface {
    @Override
    public void europeConnect() {
        System.out.println("欧洲插头连接");
    }
}

美州插头协议与实现

创建 UsaPlugInterface.java

/**
 * @author BNTang
 */
public interface UsaPlugInterface {
    /**
     * 美洲插头连接
     */
    void usaConnect();
}

创建 UsaPlug.java

/**
 * @author BNTang
 */
public class UsaPlug implements UsaPlugInterface {

    @Override
    public void usaConnect() {
        System.out.println("美洲插头充电");
    }
}

欧洲的插座

创建 EuropeSocket.java

/**
 * 欧洲的插座
 *
 * @author BNTang
 * @date 2021/08/02
 */
public class EuropeSocket {
    /**
     * 连接
     *
     * @param europePlugInterface 欧洲插头接口
     */
    void connect(EuropePlugInterface europePlugInterface) {
        europePlugInterface.europeConnect();
    }
}

客户端连接

创建 Client.java

/**
 * @author BNTang
 */
public class Client {
    public static void main(String[] args) {
        EuropeSocket europeSocket = new EuropeSocket();
        europeSocket.connect(new EuropePlug());

        // 报错
        // europeSocket.connect(new UsaPlug());
    }
}

image-20210802164947964

适配器模式

类适配器模式

创建 PlugAdapter.java

/**
 * @author BNTang
 */
public class PlugAdapter extends UsaPlug implements EuropePlugInterface {
    @Override
    public void europeConnect() {
        usaConnect();
    }
}

修改 Client.java

/**
 * @author BNTang
 */
public class Client {
    public static void main(String[] args) {
        EuropeSocket europeSocket = new EuropeSocket();
        PlugAdapter plugAdapter = new PlugAdapter();

        europeSocket.connect(plugAdapter);
    }
}

image-20210802165032546

对象适配器模式

修改 PlugAdapter.java

/**
 * @author BNTang
 */
public class PlugAdapter implements EuropePlugInterface {

    private final UsaPlug usaPlug;

    public PlugAdapter(UsaPlug usaPlug) {
        this.usaPlug = usaPlug;
    }

    @Override
    public void europeConnect() {
        usaPlug.usaConnect();
    }
}

创建 Client.java

/**
 * @author BNTang
 */
public class Client {
    public static void main(String[] args) {
        EuropeSocket europeSocket = new EuropeSocket();
        PlugAdapter plugAdapter = new PlugAdapter(new UsaPlug());

        europeSocket.connect(plugAdapter);
    }
}

image-20210802171425209

定义

  • 将一个类的接口转换成客户希望的另外一个接口
  • 使得原本由于接口不兼容而不能一起工作的那些类能一起工作

主要角色

目标(Target)接口

当前系统业务所期待的接口,它可以是抽象类或接口,EuropePlugInterface

适配者(Adaptee)类

它是被访问和适配的现存组件库中的组件接口,USAPlug

适配器(Adapter)类

它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者,PlugAdapter

UML

image-20210802180323837

image-20210802180409567

优缺点

优点

  • 允许两个或多个不兼容的对象进行交互和通信
  • 复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类
  • 将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题
  • 在很多业务场景中符合开闭原则

缺点

  • 适配器编写过程需要结合业务场景全面考虑,可能会增加系统的复杂性
  • 增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱

适用场景

  • 有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式
  • 系统需要复用现有类,而该类的接口不符合系统的需求,可以使用适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
  • 多个组件功能类似,但接口不统一且可能会经常切换时,可使用适配器模式,使得客户端可以以统一的接口使用它们

在源码中的应用

日志框架 slf4j

  • 日志框架 slf4j 晚于一些主流日志框架
  • 它定义了日志接口 org.slf4j.Logger,其中包含了日志记录的 api
  • slf4j 为了能适配其他日志框架,提供了各种适配库
  • 比如为了适配 log4j,在 slf4j-log4j.jar 包中提供 org.slf4j.impl.Log4jLoggerAdapter 记录日志的适配类
  • Log4jLoggerAdapter 继承自 org.slf4j.spi.LocationAwareLogger 继承自 org.slf4j.Logger
  • Log4jLoggerAdapter 持有了 org.apache.log4j.Logger 对象,完成了用该 log4j 的日志对象实现 slf4j 的日志接口中方法的适配

image-20210802192914473

在 SpringMVC 中的应用

在 SpringMVC 的主控制器中,SpringMVC 中处理控制器方法

image-20210802193724678

mappedHandler.getHandler() 得到的是 Controller 对象,此处并非采用直接调用 .handlerRequest 或者 MultiActionController 中编写的自定义方法,采用了一个 HandlerAdapter 的接口,为什么要使用适配器呢?此处采用了适配器模式, 由于 Controller 的类型不同,有多重实现方式,那么调用方式就不是确定的,如果需要直接调用 Controller 方法。

image-20210802194332759

假设如果我们增加一个 HardController,就要在代码中加入一行 if(mappedHandler.getHandler() instanceof HardController) 这种形式就使得程序难以维护,Spring 定义了一个适配接口,使得每一种 Controller 有一种对应的适配器实现类,让适配器代替 controller 执行相应的方法。这样在扩展 Controller 时,只需要增加一个适配器类就完成了 SpringMVC 的扩展了。每一种 Controller 创建对象控制器。

image-20210802194727289

image-20210802194749698

调用

image-20210802194844825

posted @ 2021-08-02 19:05  BNTang  阅读(78)  评论(0编辑  收藏  举报