一、故事解说:如何用苹果充电器给安卓手机充电
假设你遇到一个问题:
-
接口不兼容:苹果手机用 Lightning 接口,安卓手机用 Type-C 接口;
-
解决方案:
- 买一个 Lightning 转 Type-C 的适配器;
- 适配器一端接苹果充电器,另一端接安卓手机;
- 充电器输出的电力通过适配器转换后,就能被安卓手机接收;
-
核心逻辑:适配器将一个接口(Lightning)转换成另一个接口(Type-C),使原本不兼容的设备可以协同工作。
适配器模式核心:将一个类的接口转换成客户希望的另一个接口,让原本接口不兼容的类可以一起工作,就像充电器适配器一样。
二、适配器模式核心结构(充电器案例)
java
// 1. 目标接口:安卓手机需要的Type-C接口
interface TypeC {
void chargeWithTypeC(); // Type-C充电方法
}
// 2. 适配者:苹果充电器(提供Lightning接口)
class AppleCharger {
public void chargeWithLightning() {
System.out.println("苹果充电器通过Lightning接口输出电力");
}
}
// 3. 适配器:Lightning转Type-C
class LightningToTypeCAdapter implements TypeC {
private AppleCharger appleCharger; // 持有被适配的对象
public LightningToTypeCAdapter(AppleCharger appleCharger) {
this.appleCharger = appleCharger;
}
@Override
public void chargeWithTypeC() {
System.out.println("适配器:将Lightning接口转换为Type-C接口");
appleCharger.chargeWithLightning(); // 调用被适配对象的方法
}
}
// 4. 客户端:安卓手机
class AndroidPhone {
private TypeC typeCCharger;
public AndroidPhone(TypeC typeCCharger) {
this.typeCCharger = typeCCharger;
}
public void charge() {
System.out.println("安卓手机准备充电...");
typeCCharger.chargeWithTypeC();
System.out.println("充电完成");
}
}
// 5. 测试代码
public class Client {
public static void main(String[] args) {
// 创建苹果充电器
AppleCharger appleCharger = new AppleCharger();
// 创建适配器
TypeC adapter = new LightningToTypeCAdapter(appleCharger);
// 创建安卓手机,使用适配器充电
AndroidPhone androidPhone = new AndroidPhone(adapter);
androidPhone.charge();
// 输出:
// 安卓手机准备充电...
// 适配器:将Lightning接口转换为Type-C接口
// 苹果充电器通过Lightning接口输出电力
// 充电完成
}
}
三、Android 常用适配器模式案例与实现
案例 1:ListView/RecyclerView 的 Adapter(数据适配)
java
// 1. 目标接口:Android的ListAdapter(简化版)
interface ListAdapter {
int getCount();
Object getItem(int position);
View getView(int position, View convertView, ViewGroup parent);
}
// 2. 适配者:业务数据(如用户列表)
class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
// 3. 适配器:将User数据适配到ListAdapter
class UserListAdapter implements ListAdapter {
private List<User> userList;
private Context context;
public UserListAdapter(Context context, List<User> userList) {
this.context = context;
this.userList = userList;
}
@Override
public int getCount() {
return userList.size();
}
@Override
public Object getItem(int position) {
return userList.get(position);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// 复用convertView或创建新View
ViewHolder holder;
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(android.R.layout.simple_list_item_2, parent, false);
holder = new ViewHolder();
holder.text1 = convertView.findViewById(android.R.id.text1);
holder.text2 = convertView.findViewById(android.R.id.text2);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
// 绑定数据
User user = userList.get(position);
holder.text1.setText(user.getName());
holder.text2.setText("年龄: " + user.getAge());
return convertView;
}
private static class ViewHolder {
TextView text1;
TextView text2;
}
}
// 4. 在Activity中使用
public class MainActivity extends AppCompatActivity {
private ListView listView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = findViewById(R.id.listView);
// 准备数据
List<User> userList = new ArrayList<>();
userList.add(new User("张三", 25));
userList.add(new User("李四", 30));
userList.add(new User("王五", 22));
// 创建适配器
ListAdapter adapter = new UserListAdapter(this, userList);
// 设置适配器
listView.setAdapter((android.widget.ListAdapter) adapter);
}
}
优点:
-
解耦数据与视图:数据(User)和 ListView 接口分离,便于维护;
-
复用性强:同一数据可适配到不同 UI 组件(如 ListView、RecyclerView);
-
扩展性好:新增数据类型(如 Product)只需创建新适配器,不修改现有代码。
缺点:
- 适配器代码冗长:需要实现多个接口方法,即使部分方法不需要;
- 性能开销:频繁调用 getView () 可能影响列表滚动性能(需配合 ViewHolder 优化)。
案例 2:将第三方 SDK 接口适配为项目统一接口
java
// 1. 目标接口:项目统一的图片加载接口
interface ImageLoader {
void loadImage(String url, ImageView imageView);
}
// 2. 适配者:第三方SDK(如Glide)
class GlideSDK {
public void load(String url, ImageView imageView) {
System.out.println("Glide加载图片: " + url);
// 实际中调用Glide API
// Glide.with(context).load(url).into(imageView);
}
}
// 3. 适配器:将Glide适配到项目接口
class GlideAdapter implements ImageLoader {
private GlideSDK glideSDK;
public GlideAdapter() {
this.glideSDK = new GlideSDK();
}
@Override
public void loadImage(String url, ImageView imageView) {
glideSDK.load(url, imageView);
}
}
// 4. 客户端:Activity中使用统一接口
public class ImageActivity extends AppCompatActivity {
private ImageView imageView;
private ImageLoader imageLoader;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image);
imageView = findViewById(R.id.imageView);
// 使用适配器创建统一接口
imageLoader = new GlideAdapter();
// 加载图片(无需关心具体实现)
imageLoader.loadImage("https://example.com/image.jpg", imageView);
}
}
优点:
-
隔离第三方依赖:项目只依赖统一接口,更换 SDK(如从 Glide 到 Picasso)只需修改适配器;
-
简化接口:将复杂的第三方 API 简化为项目需要的接口;
-
便于测试:可创建模拟适配器进行单元测试,无需依赖真实 SDK。
缺点:
- 增加中间层:引入适配器会增加代码复杂度;
- 功能受限:适配器只能暴露被适配者的部分功能,可能无法使用第三方 SDK 的全部特性。
案例 3:将 JSON 数据适配为 Java 对象
java
// 1. 目标接口:项目中的用户模型
class UserModel {
private String username;
private String email;
private int age;
public UserModel(String username, String email, int age) {
this.username = username;
this.email = email;
this.age = age;
}
// Getters and setters
public String getUsername() { return username; }
public String getEmail() { return email; }
public int getAge() { return age; }
@Override
public String toString() {
return "UserModel{" +
"username='" + username + ''' +
", email='" + email + ''' +
", age=" + age +
'}';
}
}
// 2. 适配者:从网络获取的JSON数据(简化为Map)
class JsonData {
private Map<String, Object> data;
public JsonData(Map<String, Object> data) {
this.data = data;
}
public Object get(String key) {
return data.get(key);
}
}
// 3. 适配器:将JSON数据适配为UserModel
class JsonToUserAdapter {
private JsonData jsonData;
public JsonToUserAdapter(JsonData jsonData) {
this.jsonData = jsonData;
}
public UserModel adapt() {
String username = (String) jsonData.get("username");
String email = (String) jsonData.get("email");
int age = ((Double) jsonData.get("age")).intValue();
return new UserModel(username, email, age);
}
}
// 4. 客户端:处理网络数据
public class NetworkActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 模拟从网络获取的JSON数据
Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("username", "zhangsan");
jsonMap.put("email", "zhangsan@example.com");
jsonMap.put("age", 25.0);
JsonData jsonData = new JsonData(jsonMap);
// 使用适配器转换为UserModel
JsonToUserAdapter adapter = new JsonToUserAdapter(jsonData);
UserModel user = adapter.adapt();
// 使用转换后的对象
System.out.println("用户信息: " + user);
// 输出:用户信息: UserModel{username='zhangsan', email='zhangsan@example.com', age=25}
}
}
优点:
-
数据格式解耦:业务逻辑不依赖具体数据格式(JSON、XML 等),更换数据源时只需修改适配器;
-
数据清洗与转换:可在适配器中处理数据格式问题(如类型转换、字段映射);
-
提高代码健壮性:适配器可处理数据缺失、格式错误等问题,避免业务层崩溃。
缺点:
- 手动适配繁琐:复杂 JSON 结构需要编写大量适配代码(可结合 Gson 等工具简化);
- 维护成本高:当 JSON 结构变化时,需要同步修改适配器。
四、适配器模式的适用场景与总结
适用场景:
- 接口不兼容:两个类接口不匹配,但需要协同工作(如 ListView 需要 Adapter 适配数据);
- 复用旧代码:将现有类适配到新接口,无需重写旧代码(如适配第三方 SDK);
- 统一多源数据:将不同格式的数据(JSON、XML)转换为统一模型;
- 系统扩展:在不修改现有代码的情况下,适配新的实现类。
核心优点:
- 解耦接口:将目标接口与适配者接口分离,提高可维护性;
- 复用性强:可复用现有类,只需调整适配器;
- 灵活性高:可在运行时动态切换适配器;
- 符合开闭原则:新增适配器无需修改现有代码。
核心缺点:
- 代码复杂度增加:引入适配器会增加类和接口数量;
- 性能开销:多层适配可能影响性能;
- 过度使用风险:若滥用适配器,会导致系统过于复杂,难以理解和维护。
Android 中的最佳实践:
-
利用系统适配器:如 ListView/RecyclerView 的 Adapter、FragmentPagerAdapter 等;
-
适配第三方库:将不同 SDK 的接口适配为统一接口,降低切换成本;
-
数据转换:在数据层使用适配器处理不同格式的数据;
-
结合其他模式:与工厂模式结合创建适配器,与外观模式结合简化复杂适配逻辑。
适配器模式是解决接口不兼容问题的利器,在 Android 开发中广泛应用于数据适配、视图渲染、第三方库集成等场景,理解它有助于写出更灵活、可维护的代码。
编辑
分享
在Java中实现适配器模式的步骤是什么?
安卓开发中,除了充电器,还有哪些常见的适配器模式应用场景?
对比适配器模式与其他设计模式的优缺点