换了新手机,老耳机却插不上了。耳机没坏,手机也正常,就是接口对不上。
这时候一般不会拆手机改接口,也不会把好好的耳机扔了。买个转接头,接上就能听。
适配器模式干的活,差不多就是这个意思。

两边都没坏,就是接不上
新手机只接受 Type-C 耳机,代码里期待的方法叫 play()。
老耳机早就写好了,用的是另一个方法:playWithJack()。
名字不一样,调用方式也不一样。让手机直接调用老耳机,编译都过不了。
要是旧类来自别人提供的库,咱也不能说改就改。那就加一个中间层,把新接口翻译成旧调用。
手机期待的新接口
先写手机认识的接口:
public interface TypeCHeadphone {
void play();
}
手机只依赖这个接口,不关心后面接的是新耳机还是老耳机转接头。
public class Phone {
public void listen(TypeCHeadphone headphone) {
headphone.play();
}
}
这就是调用方真正想要的样子。
老耳机已经写好了
旧类的方法对不上:
public class OldHeadphone {
public void playWithJack() {
System.out.println("老耳机开始播放音乐");
}
}
这里别急着改 OldHeadphone。旧代码可能还有别的地方在用,改方法名容易牵一串。
中间加一个转接头
HeadphoneAdapter 实现手机需要的接口,同时保存一只旧耳机。
public class HeadphoneAdapter implements TypeCHeadphone {
private final OldHeadphone oldHeadphone;
public HeadphoneAdapter(OldHeadphone oldHeadphone) {
this.oldHeadphone = Objects.requireNonNull(
oldHeadphone, "oldHeadphone must not be null"
);
}
@Override
public void play() {
oldHeadphone.playWithJack();
}
}
手机调用 play(),适配器收到后改去调用 playWithJack()。
适配器没有改手机,也没有改老耳机。它只负责把两边接起来。
调用是怎么走的
把老耳机交给适配器,再把适配器交给手机:
OldHeadphone oldHeadphone = new OldHeadphone();
TypeCHeadphone adapter = new HeadphoneAdapter(oldHeadphone);
Phone phone = new Phone();
phone.listen(adapter);
输出:
老耳机开始播放音乐
调用顺序很短:
Phone.listen()
-> adapter.play()
-> oldHeadphone.playWithJack()
看着像多绕了一步,其实把不兼容的问题集中到一个类里,其他代码就干净了。
Phone 不用认识老耳机
Phone 的参数类型始终是 TypeCHeadphone。
以后换成真正的 Type-C 耳机,手机代码不用动;接旧耳机时,传入适配器就行。
调用方只面对自己需要的接口,这是这里很实用的一点。
对象适配器也比较直观:适配器内部装着旧对象,需要时把调用转过去。青岛话说,接口别扭点不要紧,接上能用就行。
啥时候适合加适配器
适配器不是改个参数名就算。
通常是两套已经存在的接口确实对不上,又不适合直接修改其中一边,才需要它。
常见情况有这些:
- 接入旧系统或第三方库;
- 新代码期待的接口和旧代码不一样;
- 希望旧类继续用,又不想改调用方;
- 转换规则想集中放在一个地方。
如果两个类本来就能直接配合,没必要硬塞一个适配器。
狗哥收个尾
适配器模式的核心很简单:接口对不上,就在中间做转换。
手机依赖 TypeCHeadphone,老耳机保留 playWithJack(),HeadphoneAdapter 把两个调用接起来。
旧代码不用推倒重写,新代码也不用知道旧接口的细节。这个转接头,算是没白加。
下一篇继续聊 桥接模式。名字也带个“接”,但解决的问题不一样。