0
0
LLDsystem_design~7 mins

Composition over inheritance in LLD - System Design Guide

Choose your learning style9 modes available
Problem Statement
When using inheritance excessively, code becomes rigid and tightly coupled. Changes in a parent class can unexpectedly break child classes, and deep inheritance trees make understanding and modifying behavior difficult.
Solution
Composition solves this by building classes from smaller, reusable components that provide specific behaviors. Instead of inheriting from a base class, objects contain instances of these components, allowing flexible combinations and easier changes without affecting unrelated parts.
Architecture
Component
Behavior A
Composite
Composite

This diagram shows a composite object containing two separate behavior components, illustrating how composition assembles functionality by combining smaller parts.

Trade-offs
✓ Pros
Improves flexibility by allowing dynamic behavior changes at runtime.
Reduces tight coupling between classes, making code easier to maintain.
Avoids problems of deep inheritance hierarchies and fragile base classes.
Encourages reuse of small, focused components across different classes.
✗ Cons
Requires more upfront design to identify reusable components.
Can lead to more objects and indirection, which might slightly impact performance.
May increase complexity if components are not well organized or documented.
Use when your system needs flexible behavior combinations or when inheritance hierarchies become complex and hard to maintain, typically in medium to large codebases.
Avoid when the system is very simple with minimal behavior variations, or when performance is critical and object indirection overhead is unacceptable.
Real World Examples
Spotify
Spotify uses composition in their client apps to combine different playback and UI behaviors dynamically without deep inheritance trees.
Netflix
Netflix applies composition in their microservices to assemble different capabilities like caching, logging, and retry logic as separate components.
Airbnb
Airbnb uses composition in their frontend React components to mix and match UI behaviors and styles flexibly.
Code Example
The before code shows inheritance where Dog and Cat extend Animal and override speak, causing tight coupling. The after code uses composition by injecting behavior objects into Animal, allowing flexible behavior changes without inheritance.
LLD
### Before: Inheritance (rigid and tightly coupled)
class Animal:
    def speak(self):
        return "Some sound"

class Dog(Animal):
    def speak(self):
        return "Bark"

class Cat(Animal):
    def speak(self):
        return "Meow"

### After: Composition (flexible and reusable)
class SpeakBehavior:
    def speak(self):
        pass

class BarkBehavior(SpeakBehavior):
    def speak(self):
        return "Bark"

class MeowBehavior(SpeakBehavior):
    def speak(self):
        return "Meow"

class Animal:
    def __init__(self, speak_behavior):
        self.speak_behavior = speak_behavior
    def speak(self):
        return self.speak_behavior.speak()

# Usage
dog = Animal(BarkBehavior())
cat = Animal(MeowBehavior())
print(dog.speak())  # Bark
print(cat.speak())  # Meow
OutputSuccess
Alternatives
Inheritance
Inheritance creates a rigid parent-child relationship where child classes extend parent behavior directly.
Use when: Choose inheritance when behavior is stable, hierarchical, and unlikely to change frequently.
Mixin classes
Mixins provide reusable behavior by multiple inheritance, but can cause complexity and conflicts.
Use when: Use mixins when you need to share behavior across unrelated classes but want to avoid deep inheritance.
Summary
Excessive inheritance creates rigid and fragile code structures that are hard to change.
Composition builds objects from reusable behavior components, improving flexibility and maintainability.
Many large companies use composition to manage complex behavior without deep inheritance trees.