Amol Pawar

Local Session Timeout

How to Implement Local Session Timeout in Financial Android Apps for Enhanced Security

In financial Android apps, setting up local session timeouts is essential to prevent unauthorized access if a user leaves the app unattended. With a session timeout, the app automatically logs the user out after a certain period of inactivity, adding a layer of security to protect sensitive data. In this blog, I’ll walk you through:...

Membership Required

You must be a member to access this content.

View Membership Levels

Already a member? Log in here
Platform Security

Ensuring Platform Security in Android : A Comprehensive Guide

These days, mobile apps—especially financial ones—are packed with sensitive data and powerful features, making security a top priority for Android developers. And it’s not just financial apps; protecting user data is now essential for every app. That’s why Google Play has introduced new guidelines focused on data security, pushing the entire Android ecosystem to be...

Membership Required

You must be a member to access this content.

View Membership Levels

Already a member? Log in here
Fingerprint Authentication

How TEE-Backed Fingerprint Authentication Works in Android for Enhanced Security

Fingerprint authentication has become a widely used method for securing mobile devices and applications. In Android, fingerprint recognition is commonly integrated to enhance security, offering a faster, more convenient way to unlock devices and authenticate transactions. But what makes fingerprint authentication on Android so secure? The answer lies in the Trusted Execution Environment (TEE) –...

Membership Required

You must be a member to access this content.

View Membership Levels

Already a member? Log in here
application security

Best Practices for Android Application Security: How to Secure Your Android App Effectively

Application security is essential in any mobile app development strategy, especially on Android, where protecting user data and app integrity is paramount. This guide explores practical security measures, like app signing, certificate checksum verification, authorized install checks, code obfuscation, and secure distribution. We’ll walk through each step with hands-on examples in Kotlin, making complex security...

Membership Required

You must be a member to access this content.

View Membership Levels

Already a member? Log in here
Design Principles

Top 4 Essential Design Principles: SOLID, DRY, KISS, and YAGNI Explained in Kotlin

Every developer wants code that’s simple to understand, easy to update, and built to last. But achieving this isn’t always easy! Thankfully, a few core principles—SOLID, DRY, KISS, and YAGNI—can help guide the way. Think of these as your trusty shortcuts to writing code that’s not only easy on the eyes but also a breeze...

Membership Required

You must be a member to access this content.

View Membership Levels

Already a member? Log in here
Liskov Substitution Principle

Many of us Misunderstand the Liskov Substitution Principle – Let’s Unfold Everything and Master LSP

In the realm of object-oriented programming, designing robust and maintainable systems is paramount. One of the foundational principles that help achieve this is the Liskov Substitution Principle (LSP). If you’ve ever dealt with class hierarchies, you’ve likely encountered situations where substitutability can lead to confusion or errors. In this blog post, we’ll break down the Liskov Substitution Principle, understand its importance, and see how to implement it effectively using Kotlin.

What is the Liskov Substitution Principle?

The Liskov Substitution Principle, named after Barbara Liskov who introduced it in 1987, states that:

If S is a subtype of T, then objects of type T should be replaceable with objects of type S without affecting the correctness of the program.

In simple words, a subclass should work in place of its superclass without causing any problems. This helps us avoid mistakes and makes our code easier to expand without bugs. For example, if you have a class Bird and a subclass Penguin, you should be able to use Penguin anywhere you use Bird without issues.

Why is Liskov Substitution Principle Important?

  1. Promotes Code Reusability: Following LSP allows developers to create interchangeable classes, enhancing reusability and reducing code duplication.
  2. Enhances Maintainability: When subclasses adhere to LSP, the code becomes easier to understand and maintain, as the relationships between classes are clearer.
  3. Reduces Bugs: By ensuring that subclasses can stand in for their parent classes, LSP helps to minimize bugs that arise from unexpected behaviors when substituting class instances.

Real-World LSP Example: Shapes

Let’s dive into an example involving shapes to illustrate LSP clearly. We’ll start by designing a base class and its subclasses, and then we’ll analyze whether the design adheres to the Liskov Substitution Principle.

The Base Class

First, we create a base class called Shape that has an abstract method for calculating the area:

Kotlin
// Shape.kt
abstract class Shape {
    abstract fun area(): Double
}

Subclasses of Shape

Now, let’s create two subclasses: Rectangle and Square.

Kotlin
// Rectangle.kt
class Rectangle(private val width: Double, private val height: Double) : Shape() {
    override fun area(): Double {
        return width * height
    }
}

// Square.kt
class Square(private val side: Double) : Shape() {
    override fun area(): Double {
        return side * side
    }
}

Using the Shapes

Next, let’s create a function to calculate the area of a shape, demonstrating how we can use both Rectangle and Square interchangeably.

Kotlin
// Main.kt
fun calculateArea(shape: Shape): Double {
    return shape.area()
}

fun main() {
    val rectangle = Rectangle(5.0, 3.0)
    val square = Square(4.0)

    println("Rectangle area: ${calculateArea(rectangle)}") // Output: 15.0
    println("Square area: ${calculateArea(square)}") // Output: 16.0
}

Now, let’s analyze: Does it follow the Liskov Substitution Principle (LSP)?

In the above code, both Rectangle and Square can be used wherever Shape is expected, and they produce correct results. This adheres to the Liskov Substitution Principle, as substituting a Shape with a Rectangle or Square doesn’t affect the program’s correctness.

Violating LSP: A Cautionary Tale

Now, let’s explore a scenario where we might inadvertently violate LSP. Imagine if we tried to implement a Square as a subclass of Rectangle:

Kotlin
// Square2.kt (Incorrect Implementation: For illustrative purposes only)
class Square2(side: Double) : Rectangle(side, side) {
    // This violates the LSP
}

Here, we try to treat Square as a special type of Rectangle. While this might seem convenient, it can cause issues, especially if we later try to set the width and height separately.

Kotlin
fun main() {
    val square = Square2(4.0)
    square.width = 5.0 // This could cause unexpected behavior
}
Leads to bugs and unexpected behavior

By trying to force a square to be a rectangle, we create scenarios where our expectations of behavior break down, violating LSP.

A Better Approach: Interfaces

To adhere to LSP more effectively, we can use interfaces instead of inheritance for our shapes:

Kotlin
// ShapeInterface.kt
interface Shape {
    fun area(): Double
}

// Rectangle.kt
class Rectangle(private val width: Double, private val height: Double) : Shape {
    override fun area(): Double {
        return width * height
    }
}

// Square.kt
class Square(private val side: Double) : Shape {
    override fun area(): Double {
        return side * side
    }
}

With this approach, we can freely create different shapes while ensuring they all adhere to the contract specified by the Shape interface.


Note: Many of us misunderstand this concept or do not fully grasp it. Many developers believe that LSP is similar to dynamic polymorphism, but this is not entirely true, as they often overlook the key part of the LSP definition: ‘without altering the correctness of the program.’

Definition: Subtypes must be substitutable for their base types without altering the correctness of the program. This means that if a program uses a base type, it should be able to work with any of its subtypes without unexpected behavior or errors.

The Liskov Substitution Principle (LSP) ensures that subclasses can replace their parent classes while maintaining the expected behavior of the program. Violating LSP can lead to unexpected bugs and issues, as subclasses may not conform to the behaviors defined by their parent classes.

Let’s understand this with a few more examples: Consider a Vehicle class with a drive function. If we create a Bicycle subclass, it may violate the Liskov Substitution Principle (LSP) because bicycles don’t ‘drive’ in the same way that cars do.

Kotlin
// Violates LSP: Bicycle shouldn't be a subclass of Vehicle
open class Vehicle {
    open fun drive() {
        // Default drive logic
    }
}

class Car : Vehicle() {
    override fun drive() {
        // Car-specific drive logic
    }
}

class Bicycle : Vehicle() {
    override fun drive() {
        throw UnsupportedOperationException("Bicycles cannot drive like cars")
    }

    fun pedal() {
        // Pedal logic
    }
}

In this example, Bicycle violates LSP because it cannot fulfill the contract of the drive method defined in Vehicle, leading to an exception when invoked.

Solution: To respect LSP, we can separate the hierarchy into interfaces that accurately represent the behavior of each type. Here’s how we can implement this:

Kotlin
interface Drivable {
    fun drive()
}

class Car : Drivable {
    override fun drive() {
        // Car-specific drive logic
    }
}

class Bicycle {
    fun pedal() {
        // Pedal logic
    }
}

Now, Car implements the Drivable interface, providing a proper implementation for drive(). The Bicycle class does not implement Drivable, as it doesn’t need to drive. Each class behaves correctly according to its nature, adhering to the Liskov Substitution Principle.

One more thing I want to add: suppose we have an Animal class and a Bird subclass.

Kotlin
open class Animal {
    open fun move() {
        println("Animal moves")
    }
}

class Bird : Animal() {
    override fun move() {
        println("Bird flies")
    }
}

In this example, Bird can replace Animal without any issues because it properly fulfills the expected behavior of the move function. When move is called on a Bird object, it produces the output “Bird flies,” which is a valid extension of the behavior defined by Animal.

This illustrates the Liskov Substitution Principle: any class inheriting from Animal should be able to act like an Animal, maintaining the expected interface and behavior.

Additional Consideration: To ensure adherence to LSP, all subclasses must conform to the expectations set by the superclass. For example, if another subclass, such as Fish, is created but its implementation of move behaves in a way that contradicts the Animal contract, it would violate LSP.


How to Avoid Violating LSP

  • Use interfaces or abstract classes that define behavior and allow different implementations.
  • Ensure that method signatures and expected behaviors remain consistent across subclasses.
  • Consider using composition over inheritance to avoid inappropriate subclassing.

Best Practices for Implementing LSP

  • Design Interfaces Thoughtfully: Design interfaces or base classes to capture only the behavior that all subclasses should have.
  • Avoid Overriding Behavior: When a method in a subclass changes expected behavior, it often signals a design issue.
  • Use Composition: When two classes share some behavior but have different constraints, use composition rather than inheritance.

Conclusion

The Liskov Substitution Principle is a fundamental concept that enhances the design of object-oriented systems. By ensuring that subclasses can be substituted for their parent classes without affecting program correctness, we create code that is more robust, maintainable, and reusable.

When designing your classes, always ask yourself: Can this subclass be used interchangeably with its parent class without altering expected behavior? If the answer is no, it’s time to reconsider your design.

Embracing LSP not only helps you write better code but also fosters a deeper understanding of your application’s architecture. So, the next time you’re faced with a class hierarchy, keep the Liskov Substitution Principle in mind, and watch your code transform into a cleaner, more maintainable version of itself!

Happy coding with LSP!

Proxy Design Pattern

The Proxy Design Pattern in Kotlin: A Comprehensive Guide

The Proxy design pattern is a structural pattern that acts as a stand-in or “placeholder” for another object, helping control access to it. By applying this pattern, you add an extra layer that manages an object’s behavior, all without altering the original object. In this article, we’ll explore the basics of the Proxy pattern, dive...

Membership Required

You must be a member to access this content.

View Membership Levels

Already a member? Log in here
Facade Design Pattern

Master the Facade Design Pattern in Kotlin: Simplifying Complex Interactions

Ever notice how every app we use—from Amazon to our banking apps—makes everything seem so effortless? What we see on the surface is just the tip of the iceberg. Beneath that sleek interface lies a mountain of complex code working tirelessly to ensure everything runs smoothly. This is where the Facade Design Pattern shines, providing...

Membership Required

You must be a member to access this content.

View Membership Levels

Already a member? Log in here
Flyweight Design Pattern

Master the Flyweight Design Pattern in Kotlin: Effortlessly Optimize Memory and Boost Game Performance

Have you ever stopped to marvel at how breathtaking mobile games have become? Think about the background graphics in popular games like PUBG or Pokémon GO. (Honest confession: I haven’t played these myself, but back in my college days, my friends and I used to have epic Counter-Strike and I.G.I. 2 sessions—and, funny enough, the...

Membership Required

You must be a member to access this content.

View Membership Levels

Already a member? Log in here
Decorator Design Pattern

Decorator Design Pattern: Unleash the Power of Dynamic Behavior Enhancement in Kotlin

The Decorator Design Pattern is a powerful structural design pattern that lets you enhance the behavior of an object on the fly, without touching the code of other objects from the same class. It’s like giving your object a superpower without changing its DNA! This approach offers a smarter alternative to subclassing, allowing you to...

Membership Required

You must be a member to access this content.

View Membership Levels

Already a member? Log in here
error: Content is protected !!