Mobile Development 11 min read

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.

Baixing.com Technical Team
Baixing.com Technical Team
Baixing.com Technical Team
Why Composition Beats Inheritance in Android List Refactoring
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.

BaseListFragment

is the parent of all list fragments, encapsulating shared data‑loading and UI‑rendering logic. Subclasses such as

ListFragmentWithFilterBar

extend 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

BaseListFragment

directly (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

IComponent

interface.

Different concrete implementations (e.g.,

List1

vs.

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

LayoutManager

after view creation, releasing resources on

onDestroyView

), making them reusable across different fragments.

2. New BaseListFragment

The new

BaseListFragment

no longer knows which components it contains; subclasses register their component instances in a

Map

that maps view IDs to

IComponent

objects. During each fragment lifecycle callback,

BaseListFragment

iterates 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

FilterBar

trigger 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>
design-patternsAndroidcomponent architecturerefactoringDependency InversionComposition over Inheritance
Baixing.com Technical Team
Written by

Baixing.com Technical Team

A collection of the Baixing.com tech team's insights and learnings, featuring one weekly technical article worth following.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.