适配器模式

适配器模式是一种结构型设计模式。用于将两个互不相关的类进行连接

首先我们来描述一个场景:QQ用来发送qq消息,微信用来发送微信消息

public interface QQ {
    void sendQQMsg();
}

public class QQImpl implements QQ {
    @Override
    public void sendQQMsg() {
        System.out.println("send by QQ");
    }

}

public interface Weixin {
    void sendWeixinMsg();
}
public class WeixinImpl implements Weixin {
    @Override
    public void sendWeixinMsg() {
        System.out.println("send by weixin");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

这时对场景进行更改,我们想要QQ也可以发送微信消息。最简单的修改就是再QQImpl中加一个发送微信的方法,代码如下

public class QQImpl implements QQ {
    @Override
    public void sendQQMsg() {
        System.out.println("send by QQ");
    }

    public void sendWeixinMsg() {
        sendQQMsg();
    }

}
1
2
3
4
5
6
7
8
9
10
11

这么改有什么隐患呢?

  • 首先,这么修改违反了开闭原则,对QQImpl类产生的修改
  • QQImpl承担了不属于它的职责,违法了单一职责原则

那么我们换一种修改方法,单独增加一个类来完成上述需求,那么这个类就要满足两个条件:

  • 这个新的类也是QQ类,因为需求要求的使qq也可以发送微信
  • 这个类的职责是使用qq发送微信消息

于是乎原代码不变,新增QQAdapter,代码如下

public class QQAdapter implements Weixin {
    private QQ qq;

    public QQAdapter(QQ qq) {
        this.qq = qq;
    }

    @Override
    public void sendWeixinMsg() {
        qq.sendQQMsg();
    }
    
}
1
2
3
4
5
6
7
8
9
10
11
12
13

总结

在上述代码中,QQAdapter被称为Adapter(适配器),QQ被称为Adaptee(适配者),Weixin被称为Target(目标物),其类图如下

使用适配器模式,最明显的两个优点就是保持了开闭原则和事Adaptee和目标类解耦

源码展示

在jdk源码中,同样用到了适配器模式,我们在使用FutureTask时,既可以使用Callable接口类作为参数,也可以使用Runnable接口类作为参数,这其中也是使用了适配器的方式,源码如下

public FutureTask(Runnable runnable, V result) {
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;       // ensure visibility of callable
}
public static <T> Callable<T> callable(Runnable task, T result) {
    if (task == null)
        throw new NullPointerException();
    return new RunnableAdapter<T>(task, result);
}
1
2
3
4
5
6
7
8
9

RunnableAdapter源码如下

static final class RunnableAdapter<T> implements Callable<T> {
    final Runnable task;
    final T result;
    RunnableAdapter(Runnable task, T result) {
        this.task = task;
        this.result = result;
    }
    public T call() {
        task.run();
        return result;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

其中Runnable和result作为Adaptee,Callable作为Target