Understanding S.O.L.I.D. Principles for Clean Android Code

Tapp AITapp AI
4 min read

The SOLID principles are a cornerstone of crafting clean, maintainable, and adaptable software. In the world of Android development, these principles become particularly important due to the often complex nature of mobile apps.

Let's break down the SOLID acronym and focus on the Single Responsibility Principle:

  • S - Single Responsibility Principle (S.R.P.): A class should have one, and only one, reason to change. Essentially, each class should be responsible for a single, well-defined task.

  • O - Open/Closed Principle (OCP): Classes should be open for extension but closed for modification. This means you should be able to add functionality without altering existing code directly.

  • L - Liskov Substitution Principle (L.S.P.): Subclasses should be seamlessly substitutable for their base classes without breaking the system's logic.

  • I - Interface Segregation Principle (I.S.P.): It's better to create many smaller, specific interfaces rather than a few large ones. Clients should not depend on interfaces they don't use.

  • D - Dependency Inversion Principle (D.I.P.): High-level modules shouldn't depend directly on low-level modules. Here, both should depend on abstractions (e.g., interfaces).

Lets start with the

S - Single Responsibility Principle (S.R.P.).

Scenario:

In a RecyclerView.Adapter class, the OnBindViewHolder method should have the singular responsibility of mapping list items to views. This aligns with the Single Responsibility Principle (S.R.P.). Complex logic or calculations should not be included within this method.

The calculation of the discounted price and subsequent formatting introduces responsibilities beyond the core duty of onBindViewHolder. This logic is better handled elsewhere, potentially in the Item model itself or a separate class dedicated to price calculations.

Let's explore how to implement the OnBindViewHolder method in alignment with the Single Responsibility Principle (S.R.P.).

O — The Open-Closed Principle (OCP):

Software modules, such as classes and functions, should be designed to facilitate the addition of new features without necessitating direct alterations to their existing code.

In other words, classes, functions, and modules should be open for extension but closed for modification.

This principle implies that when integrating new functionalities into a project, it's preferable not to modify existing code. Instead, new code should be written that interacts with the existing codebase.

For example, consider a class named TimeOfDayGreeting, containing a method called getGreetingFromTimeOfDay. This method generates a greeting message based on the time of day, to be displayed when the user opens the app.

Now, let's examine how the Open/Closed Principle (OCP) is violated in this scenario:

Now, let's examine the proper method of implementing this feature while adhering to the Open-Closed Principle (OCP).

L —The Liskov Substitution Principle (L.S.P.):

What we gather from this is that a subclass should override methods from its parent class without disrupting the parent class's functionality.

Consider this scenario: We have an interface called ClickListener. Fragments 1 and 2 implement this interface. Both fragments must implement the ClickListener interface. Our specific requirement is to increase the click count in fragment 2 while decreasing the click count in fragment 1.

Consider the example below to observe how the L.S.P. principle is breached in this situation:

Now, let's examine the proper approach to implementing this feature while adhering to the L.S.P. (Liskov Substitution Principle).

I — The Interface Segregation Principle (I.S.P.):

What I comprehend from this is that when an interface becomes overly bulky, it should be divided into smaller interfaces to avoid burdening the client with implementing unnecessary methods.

For instance, consider the TextWatcher interface in Android, which comprises three methods. This serves as a violation of the principle, as typically, we only utilize one of these methods most of the time.

Now, let's explore the proper method of implementing this feature while adhering to the I.S.P. (Interface Segregation Principle).

D — The Dependency Inversion Principle (D.I.P.):

High-level modules should not depend on low-level modules. Both should depend on abstractions.
Abstractions should not depend upon details. Details should depend upon abstractions.

If you have one class nested within another class, the nested class becomes dependent on the outer class.

For instance, imagine we have a class named JobTracker. The task at hand is to notify users via email or phone depending on the urgency of a job.

Let's examine the following example to observe how the Dependency Inversion Principle (D.I.P.) is breached in this situation:

Now, let's explore the proper method of implementing this feature while adhering to the Dependency Inversion Principle (D.I.P.).

That's a wrap! If you enjoyed this and feel I've done a solid job, please give it a clap. Your support means a lot and keeps me fired up.

Happy coding, cheers!

0
Subscribe to my newsletter

Read articles from Tapp AI directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Tapp AI
Tapp AI

Experience Tapp.ai, revolutionizing engineering education . Move beyond textbooks with hands-on learning alongside industry experts from top brands like Pepsico, Safehouse, and MNRE. Stand out with Tapp's feature for creating impressive projects and GitHub portfolios. Benefit from expert guidance, feedback, and mock interviews, setting the stage for seamless job placement. Elevate your education and career prospects with Tapp's practical approach to learning.