摘要: 适配器模式是解决接口不兼容的桥梁,通过包装对象转换为目标接口。如Android的RecyclerView.Adapter将数据适配到视图,或整合不同厂商SDK(如百度/讯飞语音)。该模式提升代码复用性,保持客户端简洁,是系统扩展的利器,符合开闭原则。
1.概念
适配器模式(Adapter Pattern) 是一种结构型设计模式,用于连接两个不兼容的接口。它通过将一个类的接口转换成客户端期望的另一个接口,解决接口不兼容问题,实现类之间的协同工作。
-
核心角色:
Target
(目标接口):客户端使用的接口Adaptee
(被适配者):需要被适配的类Adapter
(适配器):实现Target
并包装Adapter
,负责接口转换
2.在Android源码中的应用场景
经典案例:RecyclerView.Adapter
scala
// 适配器将数据集合适配到RecyclerView的视图系统中
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private List<String> mData; // 被适配的数据(Adaptee)
// 创建ViewHolder(实现Target接口)
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_view, parent, false);
return new ViewHolder(view);
}
// 绑定数据(适配过程)
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.textView.setText(mData.get(position)); // 数据 -> 视图
}
}
2.1 适配层
智仓系统:整体来看有三个角色,包括应用层、适配层服务与数据提供方 Tire1/DH) 。 其中Adapter Service 作为中间的 纽带,对上为应用层提供数据,使用 AIDL 接口跨进程通讯 。对下需要 Tire 1 /DH 接入提供数据,在 AdapterService 里面,数据提供方需要自己实现系统数据的聚合层,可能需要对接内部不 同的业务服务,具体根据各自平台的架构而定。 另外在 BeanAdapterServerSDK 内部会维护一个数据缓存,数据提供方的数据更新都会保存在缓存中, 同时上层的数据同步获取也会直接从缓存中读取,提高效率。此数据的状态准确性依赖数据提供者的状态及 时更新,对定义依赖的数据如有更新需要提供者及时向 SDK 进行更新,否则可能导致上层应用获取数据状 态不准确。
3.UML图

- 工作流程 :
Client
调用Target.request()
→Adapter
实现该方法 → 内部调用Adaptee.specificRequest()
并转换结果
4.语音项目的例子和没用设计模式的对比
在语音项目中需同时集成:
- 百度语音合成:支持在线REST API和离线SDK两种方式369
- 讯飞语音合成 :提供在线API及音频返回258
核心问题:两厂商接口设计差异大,直接调用会导致代码臃肿、维护困难。
4.1 没有使用适配器模式
arduino
public class PlayerControllerWithoutAdapter {
public void play(String text, String playerType) {
if ("baidu".equals(playerType)) {
// 直接调用百度SDK
BaiduPlayerAdapter.BaiduSdk baidu = new BaiduPlayerAdapter.BaiduSdk();
baidu.startPlay(text);
} else if ("xunfei".equals(playerType)) {
// 直接调用讯飞SDK
XunfeiPlayerAdapter.XunfeiSdk xunfei = new XunfeiPlayerAdapter.XunfeiSdk();
xunfei.playAudio(text);
}
// 新增厂商需要修改此处代码
}
// 其他方法也需要重复if-else逻辑
}

没有使用适配器模式的缺陷:
- 高耦合:业务层直接调用厂商SDK方法
- 重复代码:相同控制逻辑(如暂停/停止)需为每个厂商重写
- 方法爆炸 :控制器需要为每个厂商提供独立方法(
playBaidu()
/playXunfei()
) - 新增SDK的高成本
- SDK接口修改
4.2 使用适配器模式
csharp
// 1. 定义统一播放接口 (Target)
public interface AudioPlayer {
void play(String text);
void pause();
void stop();
void release();
}
typescript
// 2. 百度播放器适配器
public class BaiduPlayerAdapter implements AudioPlayer {
// 模拟百度SDK
public static class BaiduSdk {
public void startPlay(String content) {
System.out.println("[百度] 播放中: " + content);
}
public void pausePlay() {
System.out.println("[百度] 已暂停");
}
public void stopPlay() {
System.out.println("[百度] 已停止");
}
public void release() {
System.out.println("[百度] 资源已释放");
}
}
private final BaiduSdk baiduPlayer;
public BaiduPlayerAdapter() {
this.baiduPlayer = new BaiduSdk();
System.out.println("百度播放器适配器初始化完成");
}
@Override
public void play(String text) {
// 适配器将统一接口转换为百度SDK特定调用
baiduPlayer.startPlay(text);
}
@Override
public void pause() {
baiduPlayer.pausePlay();
}
@Override
public void stop() {
baiduPlayer.stopPlay();
}
@Override
public void release() {
baiduPlayer.release();
}
}
typescript
// 3. 讯飞播放器适配器
public class XunfeiPlayerAdapter implements AudioPlayer {
// 模拟讯飞SDK
public static class XunfeiSdk {
public void playAudio(String text) {
System.out.println("[讯飞] 正在播放: " + text);
}
public void pauseAudio() {
System.out.println("[讯飞] 暂停播放");
}
public void stopAudio() {
System.out.println("[讯飞] 停止播放");
}
public void destroy() {
System.out.println("[讯飞] 资源已销毁");
}
}
private final XunfeiSdk xunfeiPlayer;
public XunfeiPlayerAdapter() {
this.xunfeiPlayer = new XunfeiSdk();
System.out.println("讯飞播放器适配器初始化完成");
}
@Override
public void play(String text) {
// 适配器将统一接口转换为讯飞SDK特定调用
xunfeiPlayer.playAudio(text);
}
@Override
public void pause() {
xunfeiPlayer.pauseAudio();
}
@Override
public void stop() {
xunfeiPlayer.stopAudio();
}
@Override
public void release() {
xunfeiPlayer.destroy();
}
}
typescript
// 4. 播放控制器(客户端)
public class PlayerController {
private AudioPlayer currentPlayer;
public void setPlayer(AudioPlayer player) {
if (currentPlayer != null) {
currentPlayer.release();
}
currentPlayer = player;
System.out.println("播放器已切换");
}
public void playText(String text) {
if (currentPlayer == null) {
System.out.println("错误:未设置播放器");
return;
}
currentPlayer.play(text);
}
public void pause() {
if (currentPlayer != null) {
currentPlayer.pause();
}
}
public void stop() {
if (currentPlayer != null) {
currentPlayer.stop();
}
}
public void release() {
if (currentPlayer != null) {
currentPlayer.release();
currentPlayer = null;
}
}
}
csharp
// 5. 音频设备管理器(可选扩展)
public class AudioDeviceManager {
public void setOutputToSpeaker() {
System.out.println("音频输出切换到扬声器");
}
public void setOutputToHeadphones() {
System.out.println("音频输出切换到耳机");
}
}
具体的调用:
scss
// 6. 客户端使用示例
public class VoicePlayerDemo {
public static void main(String[] args) {
// 创建控制器
PlayerController controller = new PlayerController();
// 创建设备管理器
AudioDeviceManager deviceManager = new AudioDeviceManager();
// 使用百度播放器
System.out.println("\n=== 使用百度播放器 ===");
controller.setPlayer(new BaiduPlayerAdapter());
deviceManager.setOutputToSpeaker();
controller.playText("你好,欢迎使用百度语音服务");
controller.pause();
controller.playText("继续播放百度语音");
controller.stop();
// 切换到讯飞播放器
System.out.println("\n=== 切换到讯飞播放器 ===");
controller.setPlayer(new XunfeiPlayerAdapter());
deviceManager.setOutputToHeadphones();
controller.playText("科大讯飞为您服务");
controller.pause();
// 释放资源
controller.release();
}
}

5.优点
- 解耦性:分离客户端与被适配者
- 复用性:复用现有类(如不同厂商 SDK)
- 扩展性:轻松新增适配器(符合开闭原则)
- 灵活性:适配器可动态切换(如切换网络库)
6.和相似的设计模式的区别
6.1. 核心目的对比
适配器模式,策略模式,工厂模式的区别和协同使用场景! 策略模式更包含适配器模式!
arduino
// 公共接口
interface AudioPlayer {
void play(String text);
}
适配器模式和策略模式的对比
适配器模式实现
typescript
// 百度SDK原生类(不兼容接口)
class BaiduSdk {
void startBaiduPlay(String content) {
System.out.println("百度播放: " + content);
}
}
// 适配器:转换百度接口
class BaiduAdapter implements AudioPlayer {
private BaiduSdk baidu = new BaiduSdk();
@Override
public void play(String text) {
baidu.startBaiduPlay(text); // 接口转换
}
}
// 客户端使用
AudioPlayer player = new BaiduAdapter();
player.play("Hello");
策略模式实现
arduino
// 策略接口
interface PlayStrategy {
void playAudio(String text);
}
// 具体策略:百度播放
class BaiduPlayStrategy implements PlayStrategy {
@Override
public void playAudio(String text) {
System.out.println("百度策略播放: " + text);
}
}
// 上下文
class PlayerContext {
private PlayStrategy strategy;
public void setStrategy(PlayStrategy strategy) {
this.strategy = strategy;
}
public void executePlay(String text) {
strategy.playAudio(text);
}
}
// 客户端使用
PlayerContext context = new PlayerContext();
context.setStrategy(new BaiduPlayStrategy()); // 设置策略
context.executePlay("Hello");
架构图:

模式 | 核心目的 | 设计原则 |
---|---|---|
适配器模式 | 接口转换(解决不兼容问题) | 接口隔离原则 |
工厂模式 | 对象创建(封装实例化过程) | 单一职责原则 + 开闭原则 |
桥接模式 | 抽象/实现解耦(分离多维变化) | 合成复用原则 |
- 工厂模式 创建适配器对象
- 适配器模式 统一不同厂商接口
- 桥接模式 分离控制逻辑与播放实现
6.2 适配器模式和工厂模式的对比
适配器模式实现
kotlin
// 适配器解决接口差异
class BaiduAdapter : AudioPlayer {
private val baidu = BaiduSdk()
override fun play(text: String) {
baidu.startBaiduPlay(text) // 转换接口
}
}
class XunfeiAdapter : AudioPlayer {
private val xunfei = XunfeiSdk()
override fun play(text: String) {
xunfei.xfPlayAudio(text) // 转换接口
}
}
// 客户端调用
val player: AudioPlayer = BaiduAdapter()
player.play("Hello")
6.3 工厂模式实现
kotlin
// 工厂负责创建对象
object PlayerFactory {
enum class PlayerType { BAIDU, XUNFEI }
fun createPlayer(type: PlayerType): AudioPlayer {
return when(type) {
PlayerType.BAIDU -> BaiduSdkWrapper()
PlayerType.XUNFEI -> XunfeiSdkWrapper()
}
}
}
// 包装类统一接口(需预先封装)
class BaiduSdkWrapper : AudioPlayer {
private val baidu = BaiduSdk()
override fun play(text: String) {
baidu.startBaiduPlay(text)
}
}
// 客户端调用
val player = PlayerFactory.createPlayer(PlayerType.BAIDU)
player.play("Hello")
6.4 协同使用场景架构图

- 适配器模式 :关注接口转换
"如何让百度SDK的startBaiduPlay()适配到统一的play()接口?" - 策略模式 :关注行为互换
"如何在运行时切换百度播放策略和讯飞播放策略?" - 工厂模式 :关注对象创建
"如何统一创建百度播放器和讯飞播放器对象?"
总结
适配器模式在 Android 开发中应用广泛,尤其在 UI 控件(RecyclerView) 和 组件集成(音频/网络 SDK) 场景下,能有效解决接口不兼容问题,提升代码的灵活性和可维护性。通过合理使用该模式,可显著降低系统耦合度,为后续扩展留出空间。
项目的地址: github.com/pengcaihua1...