# Composition Over Inheritance: Best Practices in Android

In the realm of Object-Oriented Programming (OOP), two of the most prominent design principles are inheritance and composition. While inheritance has traditionally been the go-to approach for code reuse and extension, modern software development increasingly favors composition for its flexibility and modularity. In this blog, we’ll explore why composition is often a better choice than inheritance, and how to implement it effectively in your Android projects to create more maintainable and scalable applications.

### **Understanding Inheritance and Composition**

#### **Inheritance**

Inheritance is a mechanism in OOP that allows a class (subclass) to inherit attributes and methods from another class (superclass). It creates a hierarchical relationship between the classes and promotes code reuse.

* **Example**: A `Dog` class inherits from an `Animal` class, which means `Dog` automatically acquires the properties and methods of `Animal`.
    

```java
public class Animal {
    public void eat() {
        System.out.println("Animal is eating");
    }
}

public class Dog extends Animal {
    public void bark() {
        System.out.println("Dog is barking");
    }
}

// Usage
Dog dog = new Dog();
dog.eat(); // Inherited method
dog.bark(); // Specific to Dog
```

#### **Composition**

Composition involves creating complex types by combining objects of other types. It allows a class to contain instances of other classes, rather than inheriting from them. This approach promotes code reuse and flexibility without creating rigid class hierarchies.

* **Example**: A `Car` class can be composed of an `Engine` class and a `Wheel` class, instead of inheriting from a general `Vehicle` class.
    

```java
public class Engine {
    public void start() {
        System.out.println("Engine is starting");
    }
}

public class Wheel {
    public void rotate() {
        System.out.println("Wheel is rotating");
    }
}

public class Car {
    private Engine engine;
    private Wheel[] wheels;

    public Car() {
        engine = new Engine();
        wheels = new Wheel[]{new Wheel(), new Wheel(), new Wheel(), new Wheel()};
    }

    public void drive() {
        engine.start();
        for (Wheel wheel : wheels) {
            wheel.rotate();
        }
        System.out.println("Car is driving");
    }
}

// Usage
Car car = new Car();
car.drive(); // Output: Engine is starting, Wheel is rotating, Car is driving
```

### **Why Prefer Composition Over Inheritance?**

#### **1\. Flexibility and Reusability**

* **Inheritance**: Creates a tight coupling between classes. Changes in the superclass can have unintended effects on subclasses, making it difficult to modify and extend the hierarchy.
    
* **Composition**: Promotes loose coupling. You can easily replace or modify components without affecting other parts of the system, enhancing flexibility and reusability.
    

#### **2\. Avoiding Hierarchical Pitfalls**

* **Inheritance**: Forces a rigid hierarchy. As the class hierarchy grows, it can become difficult to manage and understand, leading to complex and brittle systems.
    
* **Composition**: Allows for more flexible and modular designs. You can compose classes in different ways to achieve desired functionality without the constraints of a fixed hierarchy.
    

#### **3\. Enhanced Testability and Maintainability**

* **Inheritance**: Makes unit testing challenging. Subclasses often inherit more than they need, leading to tightly coupled and harder-to-test code.
    
* **Composition**: Facilitates easier testing. Components can be tested in isolation, improving maintainability and allowing for more granular unit tests.
    

#### **4\. Single Responsibility Principle**

* **Inheritance**: Tends to violate the single responsibility principle by bundling multiple responsibilities into one class.
    
* **Composition**: Adheres to the single responsibility principle by delegating specific responsibilities to individual components, making the code more modular and maintainable.
    

### **Implementing Composition in Android Development**

In Android development, composition can be particularly beneficial for creating reusable and flexible components. Here are some practical examples and strategies for using composition in your projects.

#### **Example 1: Using Composition for Reusable UI Components**

Suppose you need a custom button with additional functionality, such as showing a loading indicator. Instead of extending the `Button` class, you can create a `LoadingButton` that composes a `Button` and a `ProgressBar`.

```java
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ProgressBar;

public class LoadingButton extends LinearLayout {
    private Button button;
    private ProgressBar progressBar;

    public LoadingButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    private void init(Context context) {
        button = new Button(context);
        progressBar = new ProgressBar(context);

        progressBar.setVisibility(View.GONE);

        this.setOrientation(VERTICAL);
        this.addView(button);
        this.addView(progressBar);
    }

    public void setButtonText(String text) {
        button.setText(text);
    }

    public void showLoading() {
        button.setVisibility(View.GONE);
        progressBar.setVisibility(View.VISIBLE);
    }

    public void hideLoading() {
        button.setVisibility(View.VISIBLE);
        progressBar.setVisibility(View.GONE);
    }
}

// Usage in an Activity or Fragment
LoadingButton loadingButton = findViewById(R.id.loadingButton);
loadingButton.setButtonText("Submit");
loadingButton.showLoading();
```

In this example:

* The `LoadingButton` class composes a `Button` and a `ProgressBar`, allowing for a flexible design without extending the `Button` class.
    

#### **Example 2: Using Composition for Delegating Tasks**

Composition can be used to delegate tasks to different components. For example, you can create a `NetworkManager` class that delegates network operations to a `HttpClient` class.

```java
public class HttpClient {
    public void sendRequest(String url) {
        System.out.println("Sending request to " + url);
    }
}

public class NetworkManager {
    private HttpClient httpClient;

    public NetworkManager() {
        httpClient = new HttpClient();
    }

    public void fetchData(String url) {
        httpClient.sendRequest(url);
    }
}

// Usage
NetworkManager networkManager = new NetworkManager();
networkManager.fetchData("http://example.com");
```

In this example:

* The `NetworkManager` class uses composition to delegate the task of sending a request to the `HttpClient` class, promoting separation of concerns and enhancing testability.
    

### **Best Practices for Using Composition in Android Development**

To effectively use composition in your Android projects, consider the following best practices:

#### **1\. Identify Reusable Components**

* Break down your application into smaller, reusable components. Identify common functionality that can be encapsulated into standalone classes.
    

#### **2\. Favor Composition Over Inheritance**

* When designing new classes, prioritize composition over inheritance. Use inheritance sparingly, mainly for defining truly hierarchical relationships.
    

#### **3\. Use Interfaces to Define Contracts**

* Define clear interfaces for your components to establish contracts for their behavior. This promotes flexibility and allows for easy swapping or updating of components.
    

#### **4\. Delegate Responsibilities**

* Delegate responsibilities to composed objects rather than trying to handle everything within a single class. This makes your code more modular and easier to maintain.
    

#### **5\. Promote Loose Coupling**

* Aim for loose coupling between components by using interfaces and dependency injection. This enhances flexibility and makes it easier to test and update your code.
    

#### **6\. Avoid Deep Inheritance Hierarchies**

* Keep inheritance hierarchies shallow to avoid complexity and maintainability issues. Deep hierarchies can be difficult to manage and understand.
    

### **Conclusion**

Choosing composition over inheritance is a best practice in modern software development that can greatly enhance the flexibility, maintainability, and scalability of your Android applications. By favoring composition, you create more modular and reusable code, promote loose coupling, and adhere to the single responsibility principle. Understanding when and how to use composition effectively is a key skill that will help you build robust and adaptable Android applications that are easier to develop, extend, and maintain over time.
