DEV Community

Kumar Rishi
Kumar Rishi

Posted on • Updated on

SOLID Principles for Android

Solid Principle For android

As a developer, there comes a time when we have to learn principles for moving ahead in career. For some this topic might be new, for some they are already experienced on these principles. I will try to make sure, this article may come in help for both of them.

So Let's Begin,

Every letter in SOLID , represents one principle.

The Fist Principle that we will talk about

S - Single Responsibility Principle ( SRP )

This one is easy to understand, but it might take you some time to get in memory muscle.

Real world Example - Let's say you are a chef in a restaurant, now you should be responsible for making the food right.
Now if you given also the task of handling the staffs in kitchen or serving food, will make the work complicated right.

That's how in programming, a method that you have declared should be assigned with only one responsibility and that is the only reason it should change. In this manner it will make code more convincible, workable and readable.

For ex :

class Chef{
    fun cook(){
        println("Cooking")
    }

    fun orderIngredients(){
        println("Ordering Ingredients")
    }

    fun manageStaff(){
        println("Managing Staff")
    }
}

fun main(){
    val chef = Chef()
    chef.cook()
    chef.orderIngredients()
    chef.manageStaff()
}

// but
// chef should only cook

class Chef1{
    fun cook(){
        println("Cooking")
    }
}

class Manager{
    fun orderIngredients(){
        println("Ordering Ingredients")
    }

    fun manageStaff(){
        println("Managing Staff")
    }
}

Enter fullscreen mode Exit fullscreen mode

let's take an example of common usecase, when we are handling the authentication for our app,


class LoginMethod(
    private val firebaseAuth: FirebaseAuth
) {
    fun signIn(email: String, password: String) {
        // Input validation
        if (email.isEmpty() || password.isEmpty()) {
            throw IllegalArgumentException("Invalid input")
        }

        // Authentication
        try {
            firebaseAuth.signInWithEmailAndPassword(email, password)
            println("Login successful")
        } catch (e: Exception) {
            // Error handling
            println("Login failed: ${e.message}")
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

Here we are making this signIn function to handle more responsibility than it should do, like validating the fields and handling errors also

Here's how you can fix it,

class LoginMethod(
    private val firebaseAuth: FirebaseAuth,
    private val fieldValidator: FieldValidator,
    private val errorHandler: ErrorHandler
) {
    fun signIn(email: String, password: String) {
        // Input validation
        if(!fieldValidator.validateEmail(email) || !fieldValidator.validatePassword(password)) {
            throw IllegalArgumentException("Invalid Format")
        }

        // Authentication
        try {
            firebaseAuth.signInWithEmailAndPassword(email, password)
            println("Login successful")
        } catch (e: Exception) {
            // Error handling
            errorHandler.handleError(e)
        }
    }
}

class FieldValidator {
    fun validateEmail(email: String): Boolean {
        return email.contains("@")
    }

    fun validatePassword(password: String): Boolean {
        return password.length >= 6
    }
}

class ErrorHandler {
    fun handleError(e: Exception) {
        println("An error occurred: ${e.message}")
    }
}
Enter fullscreen mode Exit fullscreen mode

Here our signIn function can only be responsible for login , not for checking validation or logging error.

Open/ Closed Principle ( OCP )

Here this principle states that, a class should open for extension but closed for modification.

Let's take an example

Real world example , say there is a car and it has music system which can play music via usb.
Now say, music system nowadays are coming with bluetooth connectivity also, then the engineers will not open each part of this music system, and install bluetooth module and close it, right. For one time it should feel convenient but for large case definitely not.
Instead they can extend one common module, where any new modules like BLE, WiFi can be connected, in this way they don't have to modify the system every time.

Let's understand with programming,

open class Shape {
    open fun draw() {
        println("Drawing Shape")
    }
}
Enter fullscreen mode Exit fullscreen mode

Here if we want to change this class for every different shape, then it might change the code for the whole app, wherever you are using this.
Here intention is we should not modify how the class behaves or works.
so in this case , the class Shape should not be modified to accomodate new types of shapes


open class Shape {
    open fun draw() {
        println("Drawing Shape")
    }
    open fun drawSquare() {
        println("Drawing Shape")
    }
    open fun drawRect() {
        println("Drawing Shape")
    }
}
Enter fullscreen mode Exit fullscreen mode

Instead you can modify this as


open class Shape {
    open fun draw() {
        println("Drawing Shape")
    }
}

class Square : Shape() {
    override fun draw() {
        println("Drawing Square")
    }
}

class Circle : Shape() {
    override fun draw() {
        println("Drawing Circle")
    }
}
Enter fullscreen mode Exit fullscreen mode

Will post other principles very soon, till then if you have any suggestion, i'm open to any discussion.

Top comments (0)