Design focuses on how to model vehicles and their features using composition instead of inheritance. Out of scope are UI design, database persistence, and network communication.
Functional Requirements
FR1: Support different types of vehicles like cars, trucks, and motorcycles
FR2: Allow vehicles to have different features such as engine types, GPS, and entertainment systems
FR3: Enable adding or removing features dynamically without changing vehicle classes
FR4: Provide a way to get vehicle details including all features
FR5: Support future extension with new vehicle types and features easily
Non-Functional Requirements
NFR1: System should be easy to maintain and extend
NFR2: Avoid deep inheritance hierarchies to reduce complexity
NFR3: Ensure code reuse without tight coupling between vehicle types and features
NFR4: Performance should be reasonable for up to 10,000 vehicles managed concurrently
Think Before You Design
Questions to Ask
❓ Question 1
❓ Question 2
❓ Question 3
❓ Question 4
❓ Question 5
Key Components
Vehicle interface or abstract class defining common vehicle behavior
Feature interfaces or classes representing individual capabilities
Composition container inside vehicle to hold features
Factory or builder to assemble vehicles with features
Client code to interact with vehicles and their features
Design Patterns
Composition design pattern
Decorator pattern for adding features dynamically
Strategy pattern for interchangeable behaviors
Interface segregation to keep features focused
Dependency injection for flexible feature assignment
Reference Architecture
+-------------------+ +-------------------+
| Vehicle |<>-----| Feature |
| (interface) | | (interface) |
+-------------------+ +-------------------+
^ ^
| |
+-------------------+ +-------------------+
| Car | | EngineFeature |
+-------------------+ +-------------------+
| Truck | | GPSFeature |
+-------------------+ +-------------------+
| Motorcycle | | EntertainmentFeature|
+-------------------+ +-------------------+
Vehicles hold a list of Features and delegate feature behavior to them.
Components
Vehicle Interface
Abstract class or interface in any OOP language
Defines common vehicle methods like getDetails()
Concrete Vehicle Classes
Classes like Car, Truck, Motorcycle
Implement Vehicle interface and hold features
Feature Interface
Interface for vehicle features
Defines methods for features like activate(), getDescription()
Concrete Feature Classes
Classes like EngineFeature, GPSFeature
Implement Feature interface with specific behavior
3. Client adds Feature objects to Vehicle's feature list
4. When client calls getDetails() on Vehicle, Vehicle calls getDescription() on each Feature
5. Vehicle aggregates feature descriptions and returns full details
6. Client can add or remove features at runtime without changing Vehicle class
Database Schema
Not applicable as this is a design pattern example focusing on code structure rather than persistence.
Scaling Discussion
Bottlenecks
Managing large numbers of features per vehicle can increase memory usage
Frequent dynamic feature changes may cause performance overhead
Complex feature interactions may require careful coordination
Without inheritance, some shared code might be duplicated if not factored well
Solutions
Use efficient data structures for feature storage like hash maps
Cache computed vehicle details if features rarely change
Define clear contracts and interfaces to manage feature interactions
Extract common code into utility classes or base components to avoid duplication
Interview Tips
Time: Spend 10 minutes understanding requirements and clarifying scope, 20 minutes designing the composition-based solution with diagrams, 10 minutes discussing scaling and trade-offs, 5 minutes summarizing.
Explain why inheritance can cause rigid and complex hierarchies
Show how composition allows flexible feature addition and removal
Describe how delegation works from vehicle to features
Discuss patterns like decorator and strategy that support composition
Mention trade-offs such as potential complexity in managing many small components
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
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 A
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
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 B
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
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 D
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:
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
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 A
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
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 C