0
0
LLDsystem_design~7 mins

Liskov Substitution Principle in LLD - System Design Guide

Choose your learning style9 modes available
Problem Statement
When a subclass cannot fully replace its parent class without causing errors or unexpected behavior, the system becomes fragile and hard to maintain. This leads to bugs when code using the parent class expects certain behavior that the subclass violates.
Solution
Ensure that subclasses can stand in for their parent classes without changing the correctness of the program. This means subclasses must honor the contracts of the parent class, including method behavior and expected outcomes, so any code using the parent class can safely use the subclass.
Architecture
ParentClass
+ operation()
SubClass
Client Code
calls op()
Uses Parent

This diagram shows client code using a parent class interface. The subclass inherits and overrides methods but must behave consistently so the client code works correctly with either.

Trade-offs
✓ Pros
Improves code reliability by preventing unexpected behavior from subclasses.
Makes code easier to maintain and extend by enforcing consistent interfaces.
Supports polymorphism, allowing flexible and reusable code.
✗ Cons
Requires careful design of class hierarchies and method contracts.
May limit subclass implementations if strict adherence is enforced.
Can increase upfront design time to ensure proper substitutability.
Use when designing class hierarchies that will be extended or reused, especially in large or evolving codebases where polymorphism is important.
When classes are simple and unlikely to be subclassed or when quick prototyping is more important than long-term maintainability.
Real World Examples
Amazon
Amazon ensures that different payment method classes can replace a generic payment interface without breaking order processing workflows.
Netflix
Netflix uses this principle to allow different streaming quality strategies to replace a base streaming class without affecting playback logic.
Uber
Uber applies this principle so various ride types can substitute a common ride interface, ensuring consistent fare calculation and booking.
Code Example
The before code violates Liskov because Ostrich cannot fly but inherits fly(), causing errors. The after code fixes this by changing the method to move(), which all birds can do differently, ensuring subclasses can replace the parent without breaking behavior.
LLD
### Before applying Liskov Substitution Principle (violating it):

class Bird:
    def fly(self):
        print("Flying")

class Ostrich(Bird):
    def fly(self):
        raise Exception("Ostriches can't fly")


def make_bird_fly(bird: Bird):
    bird.fly()

ostrich = Ostrich()
make_bird_fly(ostrich)  # Raises Exception unexpectedly


### After applying Liskov Substitution Principle (correct):

class Bird:
    def move(self):
        print("Moving")

class FlyingBird(Bird):
    def move(self):
        print("Flying")

class Ostrich(Bird):
    def move(self):
        print("Running")


def make_bird_move(bird: Bird):
    bird.move()

ostrich = Ostrich()
make_bird_move(ostrich)  # Prints "Running" safely

flying_bird = FlyingBird()
make_bird_move(flying_bird)  # Prints "Flying" safely
OutputSuccess
Alternatives
Interface Segregation Principle
Focuses on splitting interfaces into smaller, client-specific ones rather than ensuring subclass substitutability.
Use when: When clients only need parts of an interface and large interfaces cause unnecessary dependencies.
Dependency Inversion Principle
Focuses on depending on abstractions rather than concrete classes, complementing Liskov but addressing different concerns.
Use when: When you want to decouple high-level modules from low-level implementations.
Summary
Liskov Substitution Principle ensures subclasses can replace parent classes without breaking the program.
It enforces consistent behavior so client code works with any subclass transparently.
Following it improves code reliability, maintainability, and supports polymorphism.