Bird
Raised Fist0
LLDsystem_design~10 mins

Composition over inheritance in LLD - Scalability & System Analysis

Choose your learning style10 modes available
Scalability Analysis - Composition over inheritance
Growth Table: Composition over Inheritance
Users / ScaleDesign ComplexityCode ReuseFlexibilityMaintenance Effort
100 usersSimple class hierarchies, few componentsBasic reuse via inheritanceLow flexibility, rigid structuresLow effort, easy to track
10,000 usersMore components, inheritance chains growReuse limited by tight couplingHarder to change behavior without side effectsModerate effort, risk of bugs
1,000,000 usersComplex hierarchies cause code duplication and rigidityInheritance limits reuse, causes fragilityLow flexibility, difficult to extendHigh effort, frequent regressions
100,000,000 usersInheritance leads to brittle, unmaintainable codeReuse blocked by deep couplingVery low flexibility, hard to scale featuresVery high effort, costly fixes

Using composition instead allows building flexible, reusable components that can be combined without deep inheritance chains, improving scalability and maintainability at all scales.

First Bottleneck

As the system grows, the first bottleneck is the rigid class hierarchy caused by inheritance. It limits flexibility and code reuse because changes in a parent class can unexpectedly affect many child classes. This leads to increased bugs and slows down development, especially when new features or changes are needed.

Scaling Solutions
  • Use Composition: Build small, focused components that can be combined. This reduces tight coupling and makes code easier to change.
  • Favor Interfaces or Protocols: Define clear contracts for components to interact without depending on concrete implementations.
  • Apply Dependency Injection: Inject dependencies to increase flexibility and testability.
  • Modular Design: Organize code into modules or services that encapsulate behavior and state.
  • Refactor Inheritance: Replace deep inheritance trees with composition to avoid fragile base class problems.
Back-of-Envelope Cost Analysis

While composition itself is a design principle and not a direct resource consumer, its impact on system scalability is significant:

  • Development Speed: Composition reduces bugs and rework, saving developer hours as user base grows.
  • Maintenance Cost: Easier to update components independently, lowering long-term costs.
  • System Performance: Flexible components can be optimized or replaced without affecting entire system.
  • Example: At 1M users, a rigid inheritance system might cause 20% more bug fixes and slower feature rollout, increasing costs by thousands of developer hours annually.
Interview Tip

When discussing scalability, explain how inheritance can cause tight coupling and rigidity, leading to maintenance challenges as the system grows. Then describe how composition allows building flexible, reusable components that scale better. Use simple examples like "instead of a deep family tree of classes, we build small parts that can be mixed and matched." This shows you understand both design principles and their impact on system scalability.

Self Check

Question: Your system uses deep inheritance hierarchies and the codebase is hard to maintain as traffic grows 10x. What do you do first?

Answer: Refactor the design to use composition over inheritance. Break down large classes into smaller, reusable components that can be combined. This reduces coupling and makes the system easier to extend and maintain under higher load.

Key Result
Inheritance causes rigid, tightly coupled code that breaks as user scale grows; adopting composition creates flexible, reusable components that scale and maintain better.

Practice

(1/5)
1. What is the main advantage of using composition over inheritance in system design?
easy
A. It allows combining simple parts to build complex behavior flexibly.
B. It forces a strict class hierarchy that is easier to follow.
C. It eliminates the need for interfaces or contracts.
D. It automatically improves system performance.

Solution

  1. Step 1: Understand composition concept

    Composition means building complex systems by combining smaller, reusable parts.
  2. Step 2: Compare with inheritance

    Inheritance creates rigid class hierarchies, while composition offers flexibility and modularity.
  3. Final Answer:

    It allows combining simple parts to build complex behavior flexibly. -> Option A
  4. Quick Check:

    Composition = Flexible combination [OK]
Hint: Composition means combining parts, inheritance means extending classes [OK]
Common Mistakes:
  • Thinking inheritance is always more flexible
  • Confusing composition with interface implementation
  • Believing composition removes need for design contracts
2. Which of the following code snippets correctly demonstrates composition in a class design?
easy
A. class Car extends Engine { }
B. class Engine { } class Car { Engine engine; }
C. class Car implements Engine { }
D. class Engine extends Car { }

Solution

  1. Step 1: Identify composition syntax

    Composition means a class contains an instance of another class as a member.
  2. Step 2: Analyze options

    class Engine { } class Car { Engine engine; } shows Car has an Engine instance, which is composition. Others use inheritance or interface incorrectly.
  3. Final Answer:

    class Engine { } class Car { Engine engine; } -> Option B
  4. Quick Check:

    Composition = class has member object [OK]
Hint: Composition means 'has-a' relationship, not 'is-a' [OK]
Common Mistakes:
  • Confusing inheritance (extends) with composition
  • Using implements keyword incorrectly for composition
  • Assuming inheritance is composition
3. Given the following code, what will be the output?
class Battery {
  int capacity = 100;
}
class Phone {
  Battery battery = new Battery();
  int getBatteryCapacity() {
    return battery.capacity;
  }
}
Phone p = new Phone();
System.out.println(p.getBatteryCapacity());
medium
A. Compilation error
B. 0
C. NullPointerException
D. 100

Solution

  1. Step 1: Understand composition usage

    Phone class contains a Battery instance initialized with capacity 100.
  2. Step 2: Trace method call

    Calling getBatteryCapacity returns battery.capacity which is 100.
  3. Final Answer:

    100 -> Option D
  4. Quick Check:

    Composition returns member value correctly [OK]
Hint: Composition accesses member objects directly [OK]
Common Mistakes:
  • Assuming battery is null without initialization
  • Confusing inheritance with composition access
  • Expecting compilation error due to missing constructor
4. Identify the error in the following code that tries to use composition:
class Screen { }
class Laptop {
  Screen screen;
  Laptop() {
    screen = null;
  }
  void display() {
    System.out.println(screen.toString());
  }
}
medium
A. screen is not initialized before use, causing NullPointerException
B. Screen class should extend Laptop for composition
C. display method should return a value, not print
D. Laptop constructor should not assign null to screen

Solution

  1. Step 1: Check initialization of composed object

    screen is assigned null in constructor, so it is not a valid object.
  2. Step 2: Analyze method usage

    Calling screen.toString() on null causes NullPointerException at runtime.
  3. Final Answer:

    screen is not initialized before use, causing NullPointerException -> Option A
  4. Quick Check:

    Null object in composition causes runtime error [OK]
Hint: Always initialize composed objects before use [OK]
Common Mistakes:
  • Thinking inheritance is needed for composition
  • Ignoring null initialization issues
  • Confusing method return type with composition error
5. You are designing a payment system where different payment methods (credit card, PayPal, crypto) share common steps but have unique processing. How does using composition over inheritance improve your design?
hard
A. Composition requires duplicating code for each payment method, increasing maintenance.
B. Inheritance forces all payment methods to share the same code, reducing flexibility.
C. By composing payment method objects, you can add or change methods without altering a deep class hierarchy.
D. Inheritance allows runtime swapping of payment methods more easily than composition.

Solution

  1. Step 1: Understand payment system requirements

    Different payment methods share steps but differ in processing details.
  2. Step 2: Analyze composition benefits

    Composition lets you build payment methods as separate objects combined in the system, enabling easy addition or change without deep inheritance.
  3. Step 3: Contrast with inheritance

    Inheritance creates rigid hierarchies that are hard to modify and extend for new payment types.
  4. Final Answer:

    By composing payment method objects, you can add or change methods without altering a deep class hierarchy. -> Option C
  5. Quick Check:

    Composition = flexible, modular payment design [OK]
Hint: Use composition to add features without changing base classes [OK]
Common Mistakes:
  • Assuming inheritance is always simpler for new features
  • Believing composition duplicates code unnecessarily
  • Confusing runtime flexibility between inheritance and composition
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard