Why Composition Beats Inheritance in Android List Refactoring
This article explains how refactoring the Baixing Android app’s list feature from an inheritance-heavy design to a composition-based architecture improves modularity, reduces coupling, and eases future extensions by applying OOP principles such as dependency inversion and component interfaces.
One key OOP principle is to prefer composition over inheritance. While inheritance lets a subclass reuse parent code, it tightly couples the child to the parent, breaking encapsulation and making maintenance harder as the hierarchy grows. This article reviews the refactoring of the Baixing Android app’s list feature to illustrate how we applied this principle in practice.
Background
In the early Baixing Android app, list functionality was simple: fetch data from the server and render it. Initially we used an inheritance‑based structure, placing data loading and UI rendering logic in a common parent class.
In this article, the Chinese term “list” refers to a Fragment composed of
List,
FilterBar,
BottomBar, etc., while the English “List” specifically denotes the UI List control.
BaseListFragmentis the parent of all list fragments, encapsulating shared data‑loading and UI‑rendering logic. Subclasses such as
ListFragmentWithFilterBarextend it to add specific features like a filter bar.
As requirements grew, the inheritance tree became deep and unwieldy. Adding new features meant creating more subclasses, leading to a bloated hierarchy where changes to the parent affected all children.
When a new data‑loading method was needed, we faced two inheritance‑based options: modify
BaseListFragmentdirectly (risking a massive, hard‑to‑maintain parent) or create a new parent class (duplicating code across parallel hierarchies). Both approaches increased coupling and maintenance effort.
From Inheritance to Composition
Composition preserves encapsulation because components interact only through well‑designed interfaces, keeping each part a “black box.” It also adds flexibility: different component implementations can be swapped without affecting the overall structure, and composition can be decided at runtime.
Applying the Dependency Inversion Principle, we program to abstractions rather than concrete implementations. A list is built from components such as
List,
FilterBar, and
BottomBar, each defined by an interface (e.g.,
IList,
IFilterBar) that extends a common
IComponentinterface.
Different concrete implementations (e.g.,
List1vs.
List2) can be selected by a fragment at runtime, reducing coupling and allowing flexible assembly of list variants.
Implementation in Android
1. Components
Each component is essentially a wrapper around an Android
View. The component interfaces inherit from
IComponent, which mirrors the fragment lifecycle methods.
Components encapsulate their own lifecycle logic (e.g., binding a
LayoutManagerafter view creation, releasing resources on
onDestroyView), making them reusable across different fragments.
2. New BaseListFragment
The new
BaseListFragmentno longer knows which components it contains; subclasses register their component instances in a
Mapthat maps view IDs to
IComponentobjects. During each fragment lifecycle callback,
BaseListFragmentiterates over this map and forwards the call to every component.
Subclasses must:
Specify their layout.
Create required components and register them with the view‑ID map.
Establish interactions between components (e.g., let
FilterBartrigger a refresh on
List).
This composition‑based design keeps the inheritance depth to a single level, improves flexibility, and makes future feature changes easier to manage.
Conclusion
The refactoring demonstrates that replacing deep inheritance with composition reduces coupling, enhances maintainability, and allows rapid adaptation to new requirements. Understanding and applying design‑pattern principles in real projects helps developers internalize these concepts.
<code>interface IComponent {
void onCreate(Bundle savedInstanceState);
void onCreateView(View view);
void onSaveInstanceState(Bundle outState);
void onResume();
// ... other lifecycle methods
void onDestroy();
}
</code>Baixing.com Technical Team
A collection of the Baixing.com tech team's insights and learnings, featuring one weekly technical article worth following.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.