适配器模式:用充电器的故事理解接口转换

123 阅读7分钟

一、故事解说:如何用苹果充电器给安卓手机充电

假设你遇到一个问题:

  1. 接口不兼容:苹果手机用 Lightning 接口,安卓手机用 Type-C 接口;

  2. 解决方案

    • 买一个 Lightning 转 Type-C 的适配器;
    • 适配器一端接苹果充电器,另一端接安卓手机;
    • 充电器输出的电力通过适配器转换后,就能被安卓手机接收;
  3. 核心逻辑:适配器将一个接口(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 结构变化时,需要同步修改适配器。

四、适配器模式的适用场景与总结

适用场景

  1. 接口不兼容:两个类接口不匹配,但需要协同工作(如 ListView 需要 Adapter 适配数据);
  2. 复用旧代码:将现有类适配到新接口,无需重写旧代码(如适配第三方 SDK);
  3. 统一多源数据:将不同格式的数据(JSON、XML)转换为统一模型;
  4. 系统扩展:在不修改现有代码的情况下,适配新的实现类。

核心优点

  • 解耦接口:将目标接口与适配者接口分离,提高可维护性;
  • 复用性强:可复用现有类,只需调整适配器;
  • 灵活性高:可在运行时动态切换适配器;
  • 符合开闭原则:新增适配器无需修改现有代码。

核心缺点

  • 代码复杂度增加:引入适配器会增加类和接口数量;
  • 性能开销:多层适配可能影响性能;
  • 过度使用风险:若滥用适配器,会导致系统过于复杂,难以理解和维护。

Android 中的最佳实践

  • 利用系统适配器:如 ListView/RecyclerView 的 Adapter、FragmentPagerAdapter 等;

  • 适配第三方库:将不同 SDK 的接口适配为统一接口,降低切换成本;

  • 数据转换:在数据层使用适配器处理不同格式的数据;

  • 结合其他模式:与工厂模式结合创建适配器,与外观模式结合简化复杂适配逻辑。

适配器模式是解决接口不兼容问题的利器,在 Android 开发中广泛应用于数据适配、视图渲染、第三方库集成等场景,理解它有助于写出更灵活、可维护的代码。

编辑

分享

在Java中实现适配器模式的步骤是什么?

安卓开发中,除了充电器,还有哪些常见的适配器模式应用场景?

对比适配器模式与其他设计模式的优缺点