适配器设计模式有两种功能:

  1. 作为两个不兼容的接口之间的桥梁,使接口不匹配的两个类能够在一起工作
  2. 简单适配器功能

1. 功能一:兼容不兼容接口

现有如下设备:

  1. 苹果手机(Target)
  2. 苹果充电线
  3. 安卓充电线
  4. 适配器(上面有苹果充电头和安卓的插口)

image.png
安卓的手机线连不了苹果手机,所以必须通过适配器才能连接。

1.1. 类适配器

与对象适配器相同的是:把适配的类的API转换成为目标类的API。
与对象适配器不同的是:
类适配器使用继承关系连接到Adaptee(被适配类);
对象适配器使用委派关系连接到Adaptee类。

定义 Target接口代表苹果手机的接口(目标类);
定义 AppleDataLine类实现Target接口,表示苹果手机充电线可以连接苹果手机;
定义 AndroidDataLine类,表示安卓手机充电线,连接不了苹果手机(被适配类);
定义 Adapter类继承AndroidDataLine,实现Target接口,表示适配器一端连接安卓充电线,一端连接苹果手机;
Adapter和AndroidDataLine是继承关系,这决定了该适配器类型为类适配器

  1. // Target 目标接口
  2. interface Target {
  3. void iphoneConnect();
  4. }
  5. //苹果数据线
  6. class AppleDataLine implements Target{
  7. @Override
  8. public void iphoneConnect() {
  9. System.out.println("用苹果线接苹果手机");
  10. }
  11. }
  12. //安卓数据线
  13. class AndroidDataLine {
  14. public void androidConnect(){
  15. System.out.println("连接了安卓线");
  16. }
  17. }
  18. //适配器
  19. class Adapter extends AndroidDataLine implements Target{
  20. @Override
  21. public void iphoneConnect() {
  22. System.out.print("向苹果手机");
  23. super.androidConnect();
  24. }
  25. }
  26. //测试主类
  27. public class TestAdapterModule {
  28. public static void main(String[] args) {
  29. Target target = new AppleDataLine();
  30. target.iphoneConnect();
  31. Target adapter = new Adapter();
  32. adapter.iphoneConnect();
  33. }
  34. }

1.2. 对象适配器

与类适配器相同的是:把适配的类的API转换成为目标类的API。
与类适配器不同的是:
对象适配器使用委派关系连接到Adaptee类;
类适配器使用继承关系连接到Adaptee(被适配类)。

定义 Target接口代表苹果手机的接口(目标类);
定义 AppleDataLine类实现Target接口,表示苹果手机充电线可以连接苹果手机;
定义 AndroidDataLine类,表示安卓手机充电线,连接不了苹果手机(被适配类);
定义 Adapter类实现Target接口。通过有参构造传入AndroidDataLine对象,在重写的接口方法中调用AndroidDataLine对象的相应方法。

  1. // Target 目标接口
  2. interface Target {
  3. void connect();
  4. }
  5. //苹果数据线
  6. class AppleDataLine implements Target{
  7. @Override
  8. public void connect() {
  9. System.out.println("用苹果线接苹果手机");
  10. }
  11. }
  12. //安卓数据线
  13. class AndroidDataLine {
  14. public void androidConnect(){
  15. System.out.println("连接了安卓线");
  16. }
  17. }
  18. //适配器
  19. class Adapter implements Target {
  20. //被适配对象
  21. private AndroidDataLine androidDataLine;
  22. public Adapter(AndroidDataLine androidDataLine){
  23. this.androidDataLine = androidDataLine;
  24. }
  25. @Override
  26. public void connect() {
  27. //调用被适配对象的方法
  28. androidDataLine.androidConnect();
  29. }
  30. }
  31. //测试主类
  32. public class TestAdapterModule {
  33. public static void main(String[] args) {
  34. Target target = new AppleDataLine();
  35. target.connect();
  36. Target adapter = new Adapter(new AndroidDataLine());
  37. adapter.connect();
  38. }
  39. }

2. 功能二:简单适配

类C想重写接口A中的部分方法,但实现A接口就必须重写所有的方法;
所以定义抽象类B实现A接口;
C继承B,可以重写需要的部分方法。

3. 适配器模式的优缺点

3.1. 总体优缺点

优点:
1.更好的复用性
系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。
2.透明、简单
客户端可以调用同一接口,因而对客户端来说是透明的。这样做更简单,更直接。
3.更好的扩展性
在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。
4.解耦性
将目标类和适配者类解耦,通过引入一个适配器类重用现有的适配者类,而无需修改原有代码。
5.符合开放-关闭原则
同一个适配器可以把适配者类和它的子类都适配到目标接口;可以为不同的目标接口实现不同的适配器,而不需要修改待适配类。


缺点:
过多的使用适配器,会让系统非常零乱,不易整体进行把握。

3.2. 类的适配器模式优缺点

优点:仅仅引入一个对象(Adapter),不需要额外字段引用Adaptee实例。
缺点:使用对象继承的方式,是静态的定义方式。

3.3. 对象的适配器模式优缺点

优点:采用“对象组合”的方式,是动态组合。
缺点:需要引入对象实例。

4. 应用场景

4.1. 适配器的使用场景

  • 系统需要复用现有类,而该类的接口不符合系统的需求,可以使用适配器模式让接口不兼容的类一起工作;
  • 多个组件功能类似,但接口不统一会经常切换,用适配器模式,使客户端可以用统一的接口使用它们。

    4.2. 类和对象适配器模式的使用场景

    结合各自的优点:
    对象的适配器模式是对象的动态组合,更灵活,当需要适配Adaptee和Adaptee的子类时,可以使用对象的适配器模式。
    类的适配器模式更加方便,并且因为继承了Adaptee的缘故,可以重定义Adaptee的部分实现方法。

    4.3. JDK中的应用实例

    java.util.concurrent.Callable传入Thread的过程:
    1.自定义的MyThread类实现了Callable接口,作为Adaptee(被适配类);
    2.Thread类的有参构造需要传入Runnable接口的实现类;
    3.Runnable接口作为Target(目标类);
    4.FutureTask作为Runnable接口的实现类,作为Adapter(适配器);
    5.FutureTask传入Callable的实现类,通过对象的适配器模式委派MyThread对象的call()方法;
    6.将FutureTask类对象传入Thread的构造方法,构成完整的适配器模式。
    image.png ```java public class JDKInstance { public static void main(String[] args) {
    1. MyThread myThread = new MyThread();
    2. FutureTask<String> futureTask = new FutureTask<>(myThread);
    3. new Thread(futureTask).start();
    } }

class MyThread implements Callable{ @Override public String call() throws Exception { System.out.println(“test”); return “test”; } } ```