What if you could build software like LEGO blocks instead of fragile family trees?
Why Composition over inheritance in LLD? - Purpose & Use Cases
Imagine building a software system where every new feature means creating a new class that inherits from a big family tree of classes. You try to add a small change, but it forces you to rewrite or break other parts. It feels like juggling fragile glass pieces that can shatter with one wrong move.
Using inheritance heavily makes the system rigid and tangled. Changes in a parent class ripple unexpectedly to all child classes, causing bugs and confusion. It slows down development because you must understand the entire hierarchy before making even small updates.
Composition lets you build complex objects by combining simple, reusable parts. Instead of inheriting everything, you assemble behaviors and features as needed. This approach keeps code flexible, easier to understand, and safer to change without breaking unrelated parts.
class Bird extends Animal { fly() { /* fly logic */ } } class Penguin extends Bird { fly() { throw new Error('Cannot fly'); } }
class Bird { constructor(flyBehavior) { this.flyBehavior = flyBehavior; } fly() { this.flyBehavior.fly(); } } const canFly = { fly: () => console.log('Flying') }; const cannotFly = { fly: () => console.log('Cannot fly') }; const penguin = new Bird(cannotFly);
It enables building adaptable and maintainable systems by mixing and matching behaviors without deep class hierarchies.
Think of a smartphone: instead of one giant blueprint, it's made by combining parts like camera, GPS, and touchscreen modules. Each part can be upgraded or replaced independently, just like composing software features.
Inheritance creates tight, fragile class hierarchies.
Composition builds flexible objects by combining behaviors.
Composition makes code easier to change and maintain.
Practice
composition over inheritance in system design?Solution
Step 1: Understand composition concept
Composition means building complex systems by combining smaller, reusable parts.Step 2: Compare with inheritance
Inheritance creates rigid class hierarchies, while composition offers flexibility and modularity.Final Answer:
It allows combining simple parts to build complex behavior flexibly. -> Option AQuick Check:
Composition = Flexible combination [OK]
- Thinking inheritance is always more flexible
- Confusing composition with interface implementation
- Believing composition removes need for design contracts
Solution
Step 1: Identify composition syntax
Composition means a class contains an instance of another class as a member.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.Final Answer:
class Engine { } class Car { Engine engine; } -> Option BQuick Check:
Composition = class has member object [OK]
- Confusing inheritance (extends) with composition
- Using implements keyword incorrectly for composition
- Assuming inheritance is composition
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());Solution
Step 1: Understand composition usage
Phone class contains a Battery instance initialized with capacity 100.Step 2: Trace method call
Calling getBatteryCapacity returns battery.capacity which is 100.Final Answer:
100 -> Option DQuick Check:
Composition returns member value correctly [OK]
- Assuming battery is null without initialization
- Confusing inheritance with composition access
- Expecting compilation error due to missing constructor
class Screen { }
class Laptop {
Screen screen;
Laptop() {
screen = null;
}
void display() {
System.out.println(screen.toString());
}
}Solution
Step 1: Check initialization of composed object
screen is assigned null in constructor, so it is not a valid object.Step 2: Analyze method usage
Calling screen.toString() on null causes NullPointerException at runtime.Final Answer:
screen is not initialized before use, causing NullPointerException -> Option AQuick Check:
Null object in composition causes runtime error [OK]
- Thinking inheritance is needed for composition
- Ignoring null initialization issues
- Confusing method return type with composition error
Solution
Step 1: Understand payment system requirements
Different payment methods share steps but differ in processing details.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.Step 3: Contrast with inheritance
Inheritance creates rigid hierarchies that are hard to modify and extend for new payment types.Final Answer:
By composing payment method objects, you can add or change methods without altering a deep class hierarchy. -> Option CQuick Check:
Composition = flexible, modular payment design [OK]
- Assuming inheritance is always simpler for new features
- Believing composition duplicates code unnecessarily
- Confusing runtime flexibility between inheritance and composition
Ready to prove it?
Pass the test and this pattern moves unknown
