适配器模式

一、模式的定义与特点

适配器模式(Adapter)的定义如下:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。适配器模式分为类结构型模式和对象结构型模式两种,前者类之间的耦合度比后者高,且要求程序员了解选优组件库中的相关组件的内部结构,所以应用相对较少些。

优点

  • 客户端通过适配器可以透明的调用目标接口。
  • 复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类。
  • 将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题。
  • 在很多业务场景中符合开闭原则。

缺点

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

二、模式的结构与实现

类适配器模式可采用多重继承的方式来实现,Java不支持多继承,但是可以定义一个适配器类来实现当前系统的业务接口,同时又继承现有组件库中已经存在的组件。

对象适配器模式可采用将现有组件库中已经实现的组件引入适配器类中,该类同时实现当前系统的业务接口。

1. 模式的结构

适配器模式(Adapter)包含以下主要角色。

  1. 目标(Target)接口:当前系统业务所期待的接口,它可以使抽象类或接口。
  2. 适配者(Adaptee)类:它是备访问和适配的现存组件库中的组件接口。
  3. 适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。

类适配器的模式结构如下:

对象适配器模式的结构:

2. 模式的实现

(1)类适配器模式的实现

  1. /**
  2. * @Author liyuan
  3. * @Description 目标接口
  4. * @Date 16:04 2021-6-16
  5. **/
  6. public interface Target {
  7. void request();
  8. }
  9. /**
  10. * @author liyuan
  11. * @date 2021年06月16日 16:05
  12. * 适配者类
  13. */
  14. public class Adaptee {
  15. public void adapteeRequest(){
  16. System.out.println("适配者中的业务代码被调用!");
  17. }
  18. }
  19. /**
  20. * @author liyuan
  21. * @date 2021年06月16日 16:06
  22. * 适配器类
  23. */
  24. public class Adapter extends Adaptee implements Target {
  25. @Override
  26. public void request() {
  27. adapteeRequest();
  28. }
  29. }
  30. public class Test {
  31. public static void main(String[] args) {
  32. System.out.println("类适配器模式测试");
  33. Target target = new Adapter();
  34. target.request();
  35. }
  36. }

调用结果如下:

  1. 类适配器模式测试
  2. 适配者中的业务代码被调用!

(2)对象适配器模式代码如下:

/**
 * @author liyuan
 * @date 2021年06月16日 16:10
 * 对象适配器类
 */
public class ObjectAdapter implements Target {

    private Adaptee adaptee;

    public ObjectAdapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    @Override
    public void request() {
        adaptee.adapteeRequest();
    }
}

public class Test {
    public static void main(String[] args) {
        System.out.println("对象适配器模式测试");
        Adaptee adaptee = new Adaptee();
        Target target = new ObjectAdapter(adaptee);
        target.request();
    }
}

执行结果如下:

对象适配器模式测试
适配者中的业务代码被调用!

三、模式的应用实例

【例】使用适配器模式模拟新能源汽车的发动机。新能源汽车的发动机有两种,电能发动机(Electric Motor)和光能发动机(Optical Motor),电能发动机的驱动方法 electricDrive() 是用电能驱动,而光能发动机的驱动方法 opticalDrive() 是用光能驱动,它们是适配器模式中被访问的适配者。

客户端希望用统一的发动机驱动方法 drive() 访问这两种发动机,所以必须定义一个统一的目标接口 Motor,然后再定义电能适配器(Electric Adapter)和光能适配器(Optical Adapter)去适配这两种发动机。

代码实现如下:

/**
 * @Author liyuan
 * @Description 统一的驱动(目标接口)
 **/
public interface Motor {

    void drive();
}
/**
 * @author liyuan
 * @date 2021年06月16日 16:36
 * 电能发动机的驱动(电能的适配者类)
 */
public class ElectricMotor {

    public void electricDrive(){
        System.out.println("电能驱动");
    }
}
/**
 * @author liyuan
 * @date 2021年06月16日 16:38
 * 光能发动机的驱动(光能的适配者类)
 */
public class OpticalMotor {

    public void opticalDrive(){
        System.out.println("光能驱动");
    }
}
/**
 * @author liyuan
 * @date 2021年06月16日 16:35
 * 电能适配器类
 */
public class ElectricAdapter extends ElectricMotor implements Motor{
    @Override
    public void drive() {
        electricDrive();
    }
}
/**
 * @author liyuan
 * @date 2021年06月16日 16:36
 * 光能适配器类
 */
public class OpticalAdapter extends OpticalMotor implements Motor{
    @Override
    public void drive() {
        opticalDrive();
    }
}


/**
 * @author liyuan
 * @date 2021年06月16日 16:43
 * 对象适配器的实现,电能适配器类
 */
public class ElectricObjectAdapter implements Motor{
    private ElectricMotor electricMotor;

    public ElectricObjectAdapter(ElectricMotor electricMotor) {
        this.electricMotor = electricMotor;
    }

    @Override
    public void drive() {
        electricMotor.electricDrive();
    }
}
/**
 * @author liyuan
 * @date 2021年06月16日 16:44
 * 对象适配器的实现,光能适配器类
 */
public class OpticalObjectAdapter implements Motor{
    private OpticalMotor opticalMotor;

    public OpticalObjectAdapter(OpticalMotor opticalMotor) {
        this.opticalMotor = opticalMotor;
    }

    @Override
    public void drive() {
        opticalMotor.opticalDrive();
    }
}

public class Test {
    public static void main(String[] args) {
        Motor motor = new ElectricAdapter();
        motor.drive();

        Motor motor1 = new OpticalAdapter();
        motor1.drive();

        OpticalMotor opticalMotor = new OpticalMotor();
        Motor motor2 = new OpticalObjectAdapter(opticalMotor);
        motor2.drive();

        ElectricMotor electricMotor = new ElectricMotor();
        Motor motor3 = new ElectricObjectAdapter(electricMotor);
        motor3.drive();
    }
}

四、模式的应用场景

适配器模式通常由应用于以下场景:

  • 以前开发的系统存在满足新系统功能需求的类,但其接口同新系统的接口不一致。
  • 使用第三方的组件,但组件接口定义和自己要求的接口定义不同。

五、模式扩展

适配器模式(Adapter)可扩展为双向适配器模式,双向适配器类既可以把适配者接口转换成目标接口,也可以把目标接口转换成适配者接口,其结构图如图所示。

代码结构如下:

/**
 * @Author liyuan
 * @Description 目标接口
 **/
public interface TwoWayTarget {
    public void request();
}
/**
 * @author liyuan
 * @date 2021年06月16日 17:00
 * 目标实现
 */
public class TargetRealize implements TwoWayTarget{
    public void request(){
        System.out.println("目标代码被调用!");
    }
}
/**
 * @Author liyuan
 * @Description 适配者接口
 **/
public interface TwoWayAdaptee {

    public void specificRequest();
}
/**
 * @author liyuan
 * @date 2021年06月16日 17:01
 * 适配者实现
 */
public class AdapteeRealize implements TwoWayAdaptee{
    public void specificRequest()
    {
        System.out.println("适配者代码被调用!");
    }
}
/**
 * @author liyuan
 * @date 2021年06月16日 17:01
 * 双向适配器
 */
class TwoWayAdapter implements TwoWayTarget,TwoWayAdaptee{
    private TwoWayTarget target;
    private TwoWayAdaptee adaptee;
    public TwoWayAdapter(TwoWayTarget target){
        this.target=target;
    }
    public TwoWayAdapter(TwoWayAdaptee adaptee){
        this.adaptee=adaptee;
    }
    public void request(){
        adaptee.specificRequest();
    }
    public void specificRequest(){
        target.request();
    }
}


public class TwoWayTest {
    public static void main(String[] args) {
        System.out.println("目标通过双向适配器访问适配者:");
        TwoWayAdaptee adaptee=new AdapteeRealize();
        TwoWayTarget target=new TwoWayAdapter(adaptee);
        target.request();
        System.out.println("-------------------");
        System.out.println("适配者通过双向适配器访问目标:");
        target=new TargetRealize();
        adaptee=new TwoWayAdapter(target);
        adaptee.specificRequest();
    }
}

执行结果如下:

目标通过双向适配器访问适配者:
适配者代码被调用!
-------------------
适配者通过双向适配器访问目标:
目标代码被调用!