Android is SOLID

The SOLID principles acronym was introduced by Michael Feathers for the principles that were defined by Robert C. Martin (Uncle Bob) in the early 2000s.

android_solid

In order to have a quality and robust code we should have a

  • low coupling
  • high cohesion
  • strong encapsulation

These properties are achievable by applying in our code the SOLID principles, and for sure these rules could be included also in the Android development.

S.O.L.I.D. stands for:

S – Single Responsibility Principle

O – Open Closed Principle

L – Liskov Substitution Principle

I – Interface Segregation Principle

D – Dependency Inversion Principle

Let’s take a look at each principle, individually, to understand why S.O.L.I.D can help us, the developers.

1. Single Responsibility Principle

A class should have only one reason to change.

As its name suggests, this principles implies that a class/ module must do only one thing, have only one responsibility. If our class does more than one thing, then we should split the functionalities in different classes.

😡Bad code:

public class User {

    private String firstName;
    private String lastName;
    private int rating;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public int getRating() {
        return rating;
    }

    public void setRating(int rating) {
        this.rating = rating;
    }

    public boolean isVipUser() {
        return rating == 5;
    }
}

😊Good code:

public class User{

    private String firstName;
    private String lastName;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
}

public class UserType extends User{
    private int rating;
    public int getRating() {
        return rating;
    }

    public void setRating(int rating) {
        this.rating = rating;
    }
  
    public boolean isVipUser() {
        return rating == 5;
    }
}

2. Open Closed Principle

Software entities like classes, modules and functions should be open for extension but closed for modifications

We have to write code that can be adapted to new requirements without changing the old implementation. Here we should focus on extending the functionality by using interfaces.

😡Bad code:

public class Circle {
    public double radius;
}
public class Rectangle {
    public double length;
    public double width;
}
public class AreaManager {
    public double getRectangleArea(Rectangle rectangle) {
        return rectangle.length * rectangle.width;
    }

    public double getCircleArea(Circle circle) {
        return Math.PI * circle.radius * circle.radius;
    }
}

😊Good code:

public interface Shape{
    public double getArea();
}

public class Rectangle implements Shape{
    double length;
    double width;
    public double getArea(){
        return length * width;
    }
}

public class Circle implements Shape{
    public double radius;
    public double getArea(){
        return Math.PI *radius*radius;
    }
}
public class AreaManager {
    public double getShapeArea(Shape shape){
        return shape.getArea();
    }
}

3. Liskov Substitution Principle

Objects in a program should be replaceable with instances of their subtypes without changing the behaviour of that program.

The general idea of this principle is that objects should be replaceable by instances of their subtypes. This thing must be done without affecting the functionality or creating issues.

😡Bad code:

public class Toy {
    private String color;

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public void fly(){
        System.out.println("The toy is flying...");
    }
}
public class Plane extends Toy {
    @Override
    public void fly() {
        super.fly();
        System.out.println("The plane toy is flying...");
    }
}
public class Car extends Toy {
    @Override
    public void fly() {
        //we have an issue...
        super.fly();
    }
}

 😊 Good code:

public class Toy {
    private String color;

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}
public class FlyingToy extends Toy {
    public void fly(){
        System.out.println("The toy is flying...");
    }
}
public class LandToy extends Toy {
    public void move(){
        System.out.println("The toy is moving...");
    }
}
public class Plane extends FlyingToy {
    @Override
    public void fly() {
        super.fly();
        System.out.println("The plane toy is flying...");
    }
}
public class Car extends LandToy {
    @Override
    public void move() {
        super.move();
        System.out.println("The car is moving...");
    }
}

4. Interface Segregation Principle

Classes that implement interfaces, should not be forced to implement methods they do not use.

This principle is related to the fact that many specific interfaces are better than one general-purpose interface. In other words, we should not have to implement methods that we don’t use.

😡Bad code:

public interface Toy {
    void move();
    void fly();
}
public class ToyHouse implements Toy {
    String color;
   
    @Override
    public void setColor(String color) {
        this.color=color;
    }
    @Override
    public void move(){}
    @Override
    public void fly(){}
}

😊Good code:

public interface Toy {
    void setColor(String color);
}
public interface Movable {
    void move();
}
public interface Flyable {
    void fly();
}
public class ToyHouse implements Toy {
    String color;

    @Override
    public void setColor(String color) {
        this.color = color;
    }
}
public class ToyRobot implements Toy, Movable {
    String color;

    @Override
    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public void move() {
        System.out.println("ToyRobot: Start moving robot.");
    }
}

5. Dependency Inversion Principle

High level modules should not depend on low level modules rather both should depend on abstraction. Abstraction should not depend on details; rather detail should depend on abstraction

Based on this principle the interfaces should depend on other interfaces so this is important to not add concrete classes to method signatures of an interface.

😡Bad code:

public class VEngine {
 }

public class ToyCar {
    VEngine vEngine;
    ToyCar(){
        vEngine = new VEngine();
    }
}

😊Good code:

public interface IEngine {
}

public class VEngine implements IEngine{
    double cylinderCapacity;
}

public class ToyCar {
    IEngine iEngine;
    ToyCar(IEngine engine){
        iEngine = engine;
    }
}

Enjoy! Happy coding! 🙂

Feel free to leave a comment if something is not clear or if you have questions.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s