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
相关产品推荐
相关产品推荐

