You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java/Android中处理多元素的可维护性与复用性最佳实践探讨

Java/Android多元素场景的可维护性&复用性最佳实践

作为常年泡在Java/Android开发里的老鸟,碰到你说的「购物车含多个商品」「训练计划含多个动作」这类一个容器绑定多类已有元素的场景,我最推崇的是「抽象元素接口+组合模式+通用组件」这套方案,亲测能大幅提升代码的可维护性和复用性,下面拆解具体思路和原因:

一、核心落地步骤

1. 定义通用抽象接口,统一元素行为

先把所有能被添加到容器的元素(商品、训练动作、甚至未来可能加的优惠券/休息环节)抽象出一个通用接口,比如SelectableItem,只保留所有元素共有的属性和行为:

public interface SelectableItem {
    // 唯一标识,用于去重、删除等操作
    String getId();
    // 展示用的名称
    String getDisplayName();
    // 展示用的副标题(比如商品价格、动作时长)
    String getDisplaySubtitle();
    // 通用的UI绑定方法,把元素数据映射到视图
    default void bindToView(View itemView) {
        TextView nameTv = itemView.findViewById(R.id.tv_item_name);
        TextView subtitleTv = itemView.findViewById(R.id.tv_item_subtitle);
        nameTv.setText(getDisplayName());
        subtitleTv.setText(getDisplaySubtitle());
    }
}

每个具体元素(比如Product、Exercise)只需要实现这个接口,同时保留自己的特有方法即可。

2. 用泛型组合类封装容器逻辑

写一个通用的ItemContainer<T extends SelectableItem>类,把「添加/删除元素、获取列表、通用统计」这类逻辑抽离出来,不用为购物车、训练计划各写一套容器:

public class ItemContainer<T extends SelectableItem> {
    private final List<T> items = new ArrayList<>();

    // 通用添加逻辑,可加去重判断
    public void addItem(T item) {
        if (!items.stream().anyMatch(i -> i.getId().equals(item.getId()))) {
            items.add(item);
        }
    }

    public void removeItem(T item) {
        items.removeIf(i -> i.getId().equals(item.getId()));
    }

    public List<T> getItems() {
        return Collections.unmodifiableList(items);
    }

    // 用策略模式处理差异化统计(比如计算总价/总时长)
    public <R> R calculate(Calculator<T, R> calculator) {
        return calculator.calculate(items);
    }

    // 统计策略接口
    public interface Calculator<T, R> {
        R calculate(List<T> items);
    }
}

3. 通用UI组件复用列表逻辑

在Android端,写一个通用的SelectableItemAdapter适配RecyclerView,不管是购物车商品列表还是训练动作列表,都能直接复用:

public class SelectableItemAdapter extends RecyclerView.Adapter<SelectableItemAdapter.ViewHolder> {
    private final List<SelectableItem> items;
    private final int itemLayoutResId;
    private OnItemClickListener listener;

    public SelectableItemAdapter(List<SelectableItem> items, int itemLayoutResId) {
        this.items = items;
        this.itemLayoutResId = itemLayoutResId;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(itemLayoutResId, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        SelectableItem item = items.get(position);
        item.bindToView(holder.itemView);
        // 统一处理点击事件
        holder.itemView.setOnClickListener(v -> {
            if (listener != null) listener.onItemClick(item);
        });
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    public void setOnItemClickListener(OnItemClickListener listener) {
        this.listener = listener;
    }

    public interface OnItemClickListener {
        void onItemClick(SelectableItem item);
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        public ViewHolder(@NonNull View itemView) {
            super(itemView);
        }
    }
}

二、为什么选这套方案?

1. 可维护性拉满,符合开闭原则

  • 新增元素类型(比如购物车加优惠券、训练计划加休息环节):只需要新增一个SelectableItem的实现类,容器、UI代码完全不用改;
  • 修改通用逻辑(比如给所有元素加一个图标展示):只需要在SelectableItem接口里加方法,所有实现类补充实现即可,不用逐个修改容器和适配器;
  • 定位问题更简单:通用逻辑都在抽象层和容器类里,出问题不用在多个相似类里排查。

2. 复用性最大化,减少重复代码

  • 容器逻辑:ItemContainer可以直接复用在所有需要「多元素管理」的场景,不用重复写添加、删除、去重代码;
  • UI逻辑:SelectableItemAdapter适配所有实现了接口的元素,不用为每个列表写新的适配器;
  • 数据统计:通过策略模式,不用在容器类里写死「计算总价」「计算时长」这类逻辑,复用容器的同时支持差异化需求。

3. 灵活性极强,适配未来需求

比如后续要给购物车加「批量操作」,只需要在ItemContainer里加一个batchAddItems方法,所有用这个容器的场景都能直接用;如果要给训练动作加排序,只需要在容器里加排序逻辑,或者传入一个比较器即可。

三、实际使用示例

购物车场景

// 初始化购物车
ItemContainer<Product> shoppingCart = new ItemContainer<>();
// 添加商品
shoppingCart.addItem(new Product("1", "牛奶", "25.9"));
shoppingCart.addItem(new Product("2", "面包", "12.5"));
// 计算总价
double totalPrice = shoppingCart.calculate(items -> {
    double total = 0;
    for (Product p : items) {
        total += Double.parseDouble(p.getPrice());
    }
    return total;
});
// 初始化列表适配器
RecyclerView cartRv = findViewById(R.id.rv_cart);
cartRv.setAdapter(new SelectableItemAdapter(shoppingCart.getItems(), R.layout.item_cart_product));

训练计划场景

// 初始化训练计划
ItemContainer<Exercise> workoutPlan = new ItemContainer<>();
// 添加训练动作
workoutPlan.addItem(new Exercise("1", "俯卧撑", "10"));
workoutPlan.addItem(new Exercise("2", "深蹲", "15"));
// 计算总时长
int totalDuration = workoutPlan.calculate(items -> {
    int total = 0;
    for (Exercise e : items) {
        total += Integer.parseInt(e.getDuration());
    }
    return total;
});
// 初始化列表适配器
RecyclerView workoutRv = findViewById(R.id.rv_workout);
workoutRv.setAdapter(new SelectableItemAdapter(workoutPlan.getItems(), R.layout.item_workout_exercise));

内容的提问来源于stack exchange,提问作者puk789

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:49:41