Steps to Approach Object-Oriented Design Questions Tutorial from Scratch (2026)
Approaching Object-Oriented Design (OOD) questions systematically is the key to success in system design interviews. A structured method ensures you cover requirements, identify the right abstractions, and produce maintainable designs.
This guide presents a step-by-step approach to tackle any OOD problem, from requirement gathering to applying SOLID principles and design patterns. Whether you are designing a parking lot, a chess game, or a library system, these steps will serve as a reliable framework.
Requirement Gathering and Identifying Objects
Start by gathering both functional and non-functional requirements. Ask clarifying questions: Who uses this system? What actions can they take? What are the core entities? Write down explicit requirements and implicit assumptions. List all nouns as potential classes and verbs as potential methods.
Once you have the requirements, identify the key objects (classes) and their attributes. Map the relationships between them — inheritance (is-a), composition (has-a), and association (uses-a). This forms the skeleton of your class diagram.
// Step 1: Requirement gathering
// Example: Design a Parking Lot
// Requirements:
// - Multiple floors, each with spots
// - Spots: compact, large, handicapped
// - Track which spots are occupied
// - Calculate fee on exit
// - Support different payment methods
// Step 2: Identify objects and relationships
class ParkingLot {
constructor(name, address) {
this.name = name;
this.address = address;
this.floors = [];
this.rates = {};
}
addFloor(floor) { this.floors.push(floor); }
getAvailableSpots(vehicleType) {
return this.floors.reduce((count, f) =>
count + f.getAvailableSpots(vehicleType), 0);
}
parkVehicle(vehicle) {
for (const floor of this.floors) {
const spot = floor.findAvailableSpot(vehicle);
if (spot) {
spot.park(vehicle);
return new Ticket(this, spot, vehicle);
}
}
throw new Error('No spots available');
}
}
class ParkingFloor {
constructor(level) {
this.level = level;
this.spots = [];
}
addSpot(spot) { this.spots.push(spot); }
getAvailableSpots(vehicleType) {
return this.spots.filter(s => s.isAvailable && s.canFit(vehicleType)).length;
}
findAvailableSpot(vehicle) {
return this.spots.find(s => s.isAvailable && s.canFit(vehicle));
}
}
class ParkingSpot {
constructor(id, spotType) {
this.id = id;
this.spotType = spotType;
this.isAvailable = true;
this.vehicle = null;
}
canFit(vehicle) {
const compatibility = {
'motorcycle': ['compact', 'large'],
'car': ['compact', 'large'],
'truck': ['large'],
'handicapped': ['handicapped']
};
return compatibility[vehicle.type]?.includes(this.spotType);
}
park(vehicle) {
this.vehicle = vehicle;
this.isAvailable = false;
}
unpark() {
const v = this.vehicle;
this.vehicle = null;
this.isAvailable = true;
return v;
}
}
Relationships, Design Patterns, and SOLID Principles
After establishing the core objects, layer in design patterns where appropriate. Use Singleton for the ParkingLot manager, Strategy for fee calculation, Factory for creating different spot types, and Observer for notifying about spot availability. Apply SOLID principles: Single Responsibility keeps classes focused, Open-Closed allows extension via patterns, Liskov Substitution ensures spot types are interchangeable.
Finally, iterate on the design. Identify edge cases and test your design against them. Consider concurrency (if two cars try to park in the same spot), error handling (invalid tickets), and extensibility (adding new vehicle types or payment methods).
// Step 3: Design patterns and SOLID
// Strategy pattern for fee calculation
class FeeCalculator {
calculate(durationHours, spotType) {
throw new Error('Override');
}
}
class HourlyFee extends FeeCalculator {
constructor(rate) {
super();
this.rate = rate;
}
calculate(durationHours, spotType) {
return Math.ceil(durationHours) * this.rate;
}
}
class DailyFee extends FeeCalculator {
constructor(maxDaily) {
super();
this.maxDaily = maxDaily;
}
calculate(durationHours, spotType) {
const days = Math.ceil(durationHours / 24);
return Math.min(days * this.maxDaily, durationHours * 2);
}
}
// Factory for spot creation
class SpotFactory {
static createSpot(type, id) {
const spotTypes = {
compact: CompactSpot,
large: LargeSpot,
handicapped: HandicappedSpot
};
const SpotClass = spotTypes[type];
if (!SpotClass) throw new Error('Unknown spot type');
return new SpotClass(id);
}
}
// Liskov Substitution: subclasses are interchangeable
class CompactSpot extends ParkingSpot {
constructor(id) { super(id, 'compact'); }
}
class LargeSpot extends ParkingSpot {
constructor(id) { super(id, 'large'); }
}
class HandicappedSpot extends ParkingSpot {
constructor(id) { super(id, 'handicapped'); }
}
// Step 4: Complete the design with Ticket and Payment
class Ticket {
constructor(lot, spot, vehicle) {
this.lot = lot;
this.spot = spot;
this.vehicle = vehicle;
this.entryTime = new Date();
this.exitTime = null;
this.paid = false;
}
checkout(feeCalculator) {
this.exitTime = new Date();
const duration = (this.exitTime - this.entryTime) / (1000 * 60 * 60);
const fee = feeCalculator.calculate(duration, this.spot.spotType);
this.paid = true;
this.spot.unpark();
return fee;
}
}
// Usage
const lot = new ParkingLot('Downtown Garage', '123 Main St');
const floor1 = new ParkingFloor(1);
floor1.addSpot(SpotFactory.createSpot('compact', '1A'));
floor1.addSpot(SpotFactory.createSpot('large', '1B'));
lot.addFloor(floor1);
const car = { type: 'car', licensePlate: 'ABC123' };
const ticket = lot.parkVehicle(car);
const fee = ticket.checkout(new HourlyFee(3));
console.log(`Total fee: $${fee}`);
Frequently Asked Questions
What is the first step when approaching an OOD interview question?
Clarify requirements by asking questions. Understand what the system should do (functional requirements) and what qualities it should have (non-functional). Don't assume anything — confirm the scope with the interviewer. Write down explicit requirements before designing.
How do you identify classes from a problem description?
Look for nouns in the requirements — these are candidate classes. Verbs become methods. Group related attributes together. For example, in a parking lot: 'vehicle', 'parking spot', 'ticket', 'floor' are nouns; 'park', 'unpark', 'calculateFee' are verbs.
When should I introduce design patterns in the design process?
Introduce patterns after establishing the core class structure and relationships. Don't force patterns — let them emerge naturally from the design problems you encounter. Strategy for varying algorithms, Factory for object creation, Observer for notifications, and State for objects with changing behavior.
How do SOLID principles guide OOD?
Single Responsibility ensures each class has one reason to change. Open-Closed lets you extend without modifying existing code via patterns. Liskov Substitution ensures subtype interchangeability. Interface Segregation keeps interfaces focused. Dependency Inversion relies on abstractions, not concretions.
Originally published on Ayodhyyya. Last updated June 1, 2026.