Bird
Raised Fist0

When to Use Inheritance vs Composition - The Rule Every Senior Engineer Knows

LLDmediumInterview freq: high
Choose your preparation mode4 modes available
๐ŸŽฏ
When to Use Inheritance vs Composition - The Rule Every Senior Engineer Knows
40 min ยท medium
medium

Design a vehicle parking system with 5 vehicle types, 4 parking spot types, and a pricing structure. The system must handle vehicle behaviors, spot allocation, and pricing calculation while demonstrating correct use of inheritance and composition.

  • Vehicles have shared state: licensePlate and vehicleType.
  • Parking spots have types and availability status.
  • Pricing depends on vehicle and spot types with possible discounts.
  • Vehicle behaviors differ: some can park in multiple spot types, others cannot.
  • The system must be extensible to add new vehicle or spot types without modifying existing classes.
๐Ÿ“‹
Guided Steps
1/5
Step 1
Define Vehicle Hierarchy Using Inheritance
Design the Vehicle class hierarchy capturing shared state and behavior. Decide between abstract class and interface.
๐Ÿ’ก Hint
Use abstract class if you need to hold instance state like licensePlate; interfaces cannot hold fields in Java.
abstract class Vehicle(licensePlate, vehicleType) - abstract because it holds shared instance STATE; interface cannot hold fields in Java
Mid: Names abstract class for shared behavior. Senior: Explains why interface cannot hold instance fields and how abstract class enables code reuse and state encapsulation.
โš ๏ธCommon Wrong Answer
Using interface Vehicle with fields leads to compile-time error; or using interface forces duplicating state in subclasses, violating DRY.
Step 2
Model ParkingSpot Using Composition
Design ParkingSpot class and relate it to Vehicle. Should ParkingSpot inherit Vehicle or use composition? Justify.
๐Ÿ’ก Hint
Use composition to model 'has-a' relationship; inheritance implies 'is-a' which is incorrect here.
ParkingSpot has a Vehicle reference (composition) - because a spot contains a vehicle but is not a vehicle; composition models lifecycle ownership where ParkingSpot controls Vehicle presence.
Mid: Identifies composition over inheritance. Senior: Explains lifecycle ownership and how composition allows flexible spot-vehicle association without violating Liskov Substitution Principle.
โš ๏ธCommon Wrong Answer
Using inheritance ParkingSpot extends Vehicle leads to incorrect 'is-a' relationship and breaks SOLID Liskov Substitution Principle.
Step 3
Implement Pricing Strategy Using Interface and Composition
Design pricing calculation to support multiple pricing rules. Should pricing be inheritance or composition-based?
๐Ÿ’ก Hint
Use interface PricingStrategy and compose it inside ParkingLot or Vehicle to allow flexible pricing without modifying existing classes.
interface PricingStrategy with method calculatePrice(Vehicle v, ParkingSpot s) - interface used because no state needed; composed inside ParkingLot for extensibility (Open/Closed Principle).
Mid: Uses Strategy pattern with interface. Senior: Explains interface's statelessness and how composition enables runtime pricing strategy changes without inheritance rigidity.
โš ๏ธCommon Wrong Answer
Using inheritance for pricing leads to rigid hierarchy and violates Open/Closed Principle; also interfaces cannot hold pricing state if needed.
Step 4
Ensure Thread-Safe Spot Allocation
Design parkVehicle method to allocate spots safely in concurrent environment. How to prevent race conditions?
๐Ÿ’ก Hint
Use synchronized method or locks to atomically check and set spot availability; volatile alone is insufficient for atomicity.
public synchronized boolean parkVehicle(Vehicle v) - synchronized ensures atomic check-and-set of spot availability; volatile spot status alone cannot prevent race conditions.
Mid: Names synchronized for mutual exclusion. Senior: Traces thread interleaving where Thread 1 reads AVAILABLE, context switch, Thread 2 reads AVAILABLE, both park causing double-booking without synchronization.
โš ๏ธCommon Wrong Answer
Using volatile boolean isAvailable without synchronization allows race condition where multiple threads park in same spot.
Step 5
Extend System Without Modifying Existing Classes
How to add new vehicle or spot types without changing existing code? Which SOLID principle applies?
๐Ÿ’ก Hint
Use inheritance for new vehicle types extending abstract Vehicle; use composition to add new behaviors; Open/Closed Principle mandates extension without modification.
Use inheritance to extend abstract Vehicle for new types; use composition to add behaviors; Open/Closed Principle enforced by abstract class and composition over inheritance.
Mid: Names Open/Closed Principle. Senior: Explains how abstract class allows extension and composition avoids modifying existing classes, preventing regression bugs.
โš ๏ธCommon Wrong Answer
Modifying existing classes to add new types violates Open/Closed Principle and risks introducing bugs.
๐Ÿ”Œ
API Design Challenge
Design an API for the parking system that allows clients to park vehicles, check spot availability, and calculate pricing dynamically. The API should support extensibility for new vehicle and spot types without breaking existing clients.
  • Use interfaces and abstract classes to define contracts and shared behavior.
  • Design methods that accept enums for vehicle and spot types to avoid string errors.
  • Ensure thread safety in methods that modify shared state like spot allocation.
๐Ÿ“Š
Scoring Rubric
โ–ผ Show
Must Have - 60%
  • abstract class Vehicle with instance state licensePlate and vehicleType
  • ParkingSpot uses composition to hold Vehicle reference
  • PricingStrategy interface for pricing calculation
  • synchronized parkVehicle method for thread-safe spot allocation
  • Use enum for VehicleType and SpotType
Good to Have - 30%
  • Singleton ParkingLot with volatile instance and double-checked locking
  • Open/Closed Principle adherence for extensibility
  • Avoid inheritance for unrelated concepts respecting Liskov Substitution Principle
Bonus - 10%
  • Dynamic behavior addition via composition
  • Detailed concurrency explanation with thread interleaving
String for VehicleType/SpotType leads to runtime typo bugs and poor IDE support; enum prevents this at compile time
Using interface to hold instance state causes compile-time errors; abstract class needed for shared state
Inheritance ParkingSpot extends Vehicle creates incorrect 'is-a' relationship violating Liskov Substitution Principle
Volatile boolean without synchronization allows race conditions causing double-booking
Modifying existing classes to add new types violates Open/Closed Principle risking regression bugs
โœ…
Model Answer
โ–ผ Reveal

The design uses an abstract class Vehicle to encapsulate shared state (licensePlate, vehicleType) and behavior, as interfaces in Java cannot hold instance fields. ParkingSpot models a 'has-a' relationship with Vehicle using composition, avoiding incorrect inheritance. PricingStrategy is an interface defining stateless pricing behavior, composed inside ParkingLot to allow flexible pricing rules adhering to the Open/Closed Principle. The parkVehicle method is synchronized to ensure thread-safe spot allocation, preventing race conditions that volatile alone cannot handle. Enums are used for VehicleType and SpotType to prevent runtime errors from string typos. The system supports extensibility by allowing new vehicle types via inheritance and new behaviors via composition, following SOLID principles.

  • Use abstract class Vehicle to hold shared instance state because interfaces cannot hold fields in Java
  • Model ParkingSpot with composition to hold Vehicle reference, reflecting lifecycle ownership and avoiding incorrect 'is-a' relationship
  • Implement PricingStrategy as an interface and compose it inside ParkingLot to allow flexible, stateless pricing strategies
  • Synchronize parkVehicle method to atomically check and set spot availability, preventing race conditions in concurrent environments
  • Use enums for VehicleType and SpotType to avoid runtime errors from string typos and enable exhaustive switch statements
  • Enforce Open/Closed Principle by extending abstract Vehicle for new types and using composition for adding behaviors without modifying existing classes
  • Avoid inheritance between unrelated concepts to respect Liskov Substitution Principle
  • Use volatile with double-checked locking for thread-safe lazy initialization of singleton ParkingLot
  • abstract class Vehicle holds licensePlate and vehicleType state; interface cannot hold instance fields in Java
  • ParkingSpot uses composition to hold Vehicle reference, modeling lifecycle ownership correctly
  • PricingStrategy interface defines stateless pricing behavior; composed inside ParkingLot for flexibility
  • parkVehicle method synchronized to atomically check and set spot availability, preventing race conditions
  • Use enum for VehicleType and SpotType to prevent runtime typo bugs and enable exhaustive switch
  • Open/Closed Principle enforced by abstract Vehicle and composition for pricing and spot allocation
  • Avoid inheritance for unrelated concepts (e.g., ParkingSpot is not a Vehicle) to respect Liskov Substitution Principle
  • Volatile alone insufficient for atomic updates; synchronized needed for compound actions
  • Singleton ParkingLot with volatile instance and double-checked locking to ensure thread-safe lazy initialization
  • Use composition to add new behaviors dynamically without modifying existing classes

Practice

(1/5)
1. In a Music Streaming Library design, a Playlist class has a private constructor and a static synchronized getInstance() method that returns the single instance. Which design pattern is applied here and why is it the correct choice?
easy
A. Factory pattern, because it controls object creation centrally.
B. Builder pattern, to construct complex Playlist objects step-by-step.
C. Prototype pattern, to clone existing Playlist instances efficiently.
D. Singleton pattern, to ensure only one Playlist instance manages the playback queue globally.

Solution

  1. Step 1: Identify the pattern from private constructor and static synchronized getInstance()

    This is the Singleton pattern, which restricts instantiation to one object and provides a global access point.
  2. Step 2: Why Singleton fits Playlist playback queue management

    Since the playback queue must be globally consistent and unique per user session, Singleton ensures only one instance manages it, avoiding conflicting states.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Private constructor + synchronized getInstance() -> Singleton pattern [check]
Hint: Private constructor + static getInstance() = Singleton [check]
Common Mistakes:
2. You want to add a WeekendFeeStrategy to calculate parking fees differently on weekends. Given the current design uses a FeeStrategy interface, what is the minimum change to comply with SOLID principles?
medium
A. Modify the existing FeeStrategy implementation to add weekend logic with an if-else block.
B. Merge all fee strategies into a single class with multiple methods for each pricing rule.
C. Create a new WeekendFeeStrategy class implementing FeeStrategy and inject it where needed without modifying existing classes.
D. Add a boolean flag isWeekend to the ParkingLot class and change fee calculation accordingly.

Solution

  1. Step 1: Identify SOLID principle

    Open-Closed Principle (OCP) requires classes to be open for extension but closed for modification.
  2. Step 2: Apply OCP

    Creating a new WeekendFeeStrategy class extends functionality without modifying existing code, adhering to OCP and Dependency Inversion Principle.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    New strategy class -> extend without modifying existing -> OCP [check]
Hint: Add new strategy class, don't modify existing code [check]
Common Mistakes:
3. Adding a WeekendFeeStrategy to calculate weekend parking fees requires which minimum change to the current design that uses a FeeStrategy interface?
medium
A. Combine all fee calculations into one class with if-else for weekend and weekday fees
B. Create a new WeekendFeeStrategy class implementing FeeStrategy and inject it without modifying existing classes
C. Modify the existing FeeStrategy interface to add a isWeekend() method and update all implementations
D. Add a boolean flag isWeekend in ParkingLot and conditionally apply fees inside existing strategies

Solution

  1. Step 1: Understand current design with FeeStrategy interface

    FeeStrategy interface allows different fee calculation implementations, supporting OCP if new strategies are added as new classes.
  2. Step 2: Analyze minimal change for weekend fees

    Create a new WeekendFeeStrategy class implementing FeeStrategy and inject it without modifying existing classes respects OCP by adding a new class without modifying existing ones. Add a boolean flag isWeekend in ParkingLot and conditionally apply fees inside existing strategies modifies ParkingLot to add weekend logic, violating OCP but minimal change.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Minimal change but violates OCP -> modify ParkingLot with weekend flag [check]
Hint: Minimal change โ‰  OCP compliance; adding flag is minimal but not ideal [check]
Common Mistakes:
4. ElectricCar pieces require special ChargingSpot compatibility in the Jigsaw Puzzle System. What is the minimum design change to support ElectricCar and ChargingSpot without modifying existing classes?
hard
A. Add ELECTRIC_CAR to PieceType enum, CHARGING to SpotType enum, create ElectricCarPiece subclass, and update compatibility map externally.
B. Modify existing Piece and Spot classes to include electric-specific fields and logic.
C. Create a new ElectricCarPiece class implementing a new interface and modify Board to handle it specially.
D. Add a god-class CompatibilityManager that hardcodes all compatibility rules including electric cars.

Solution

  1. Step 1: Identify extensibility requirements

    New piece and spot types must be added without modifying existing classes to respect OCP.
  2. Step 2: Evaluate minimal change approach

    Adding new enum values and subclass for ElectricCarPiece plus updating compatibility map externally allows extension without modifying existing code.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    New enums and subclass extend system without changing existing classes [check]
Hint: Add enums + subclass + external map update = minimal invasive extension [check]
Common Mistakes:
5. The ATM system needs to support ElectricCar vehicles requiring ChargingSpot types. What is the minimum change to the current design to support this without modifying existing classes?
hard
A. Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType enum, create ElectricCar subclass, and update SpotCompatibility.COMPATIBILITY_MAP externally.
B. Modify VehicleType and SpotType enums and add compatibility logic inside existing Vehicle and Spot classes.
C. Create new ElectricCar and ChargingSpot classes and modify existing ParkingLot to handle them explicitly.
D. Add flags in Vehicle and Spot classes to indicate electric compatibility, modifying existing classes.

Solution

  1. Step 1: Identify extensibility requirements

    New vehicle and spot types must be added without modifying existing classes to respect OCP.
  2. Step 2: Apply minimal change

    Add new enum constants ELECTRIC_CAR and CHARGING. Create ElectricCar subclass. Update compatibility map externally to include new types. No existing classes modified.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    New enums + subclass + external map update -> no existing class changes [check]
Hint: Add enums + subclass + external map update -> no existing class changes [check]
Common Mistakes:
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