Identify Core Objects in LLD Interviews
TL;DR
Identifying core objects means extracting the main entities (nouns) from a problem description and determining their attributes (data) and behaviors (methods). This systematic approach transforms requirements into a class structure, forming the foundation of your object-oriented design. Mastering this skill is critical for design interviews where you must quickly translate real-world scenarios into code.
Core Concept
What Are Core Objects?
Core objects are the main entities in your system that have both state (data) and behavior (actions). They represent the “things” your application manages and manipulates. In OOP design, identifying these objects is your first step — before writing any code, you need to know what you’re building.
Think of core objects as the nouns in your problem description that need to do something or have something done to them. Not every noun becomes a class, but every class starts as a noun.
The Three-Step Process
Step 1: Extract Nouns (Potential Objects)
Read the problem statement and underline every noun. These are your candidates for objects. For example, in “A library system manages books, members, and loans,” you have: library, system, books, members, and loans.
Step 2: Identify Attributes (State)
For each potential object, ask: “What information does this thing need to know about itself?” A Book needs a title, author, ISBN. A Member needs a name, ID, contact info. These become your class attributes — the data each object holds.
Step 3: Identify Behaviors (Methods)
Ask: “What can this object do?” and “What can be done to this object?” A Book can be checked out, returned, or reserved. A Member can borrow books, return books, or pay fines. These actions become your methods.
Filtering Your Candidates
Not every noun becomes a class. Filter out:
- Attributes disguised as nouns: “Title” is not a class; it’s an attribute of Book
- Primitives: Simple data types like strings or numbers rarely need their own class
- The system itself: “Library System” is usually not an object; it’s what you’re building
- Synonyms: “User” and “Member” likely refer to the same entity
Why This Matters
Identifying core objects correctly sets up everything else. Get this wrong, and you’ll struggle with awkward designs, bloated classes, or missing functionality. Get it right, and the rest of your design flows naturally. In interviews, this is where you demonstrate your ability to think in objects rather than procedures.
Visual Guide
From Problem to Objects
graph TD
A[Problem Statement] --> B[Extract Nouns]
B --> C[Filter Candidates]
C --> D[Identify Attributes]
C --> E[Identify Behaviors]
D --> F[Core Objects with State]
E --> F
F --> G[Class Structure]
style A fill:#e1f5ff
style G fill:#c8e6c9
The systematic process of transforming a problem description into core objects with attributes and behaviors.
Library System Core Objects
classDiagram
class Book {
-title: string
-author: string
-isbn: string
-isAvailable: bool
+checkout()
+return()
+getDetails()
}
class Member {
-memberId: string
-name: string
-email: string
-borrowedBooks: List
+borrowBook()
+returnBook()
+viewHistory()
}
class Loan {
-loanId: string
-book: Book
-member: Member
-dueDate: date
+calculateFine()
+extend()
+complete()
}
Member "1" -- "*" Loan
Book "1" -- "*" Loan
Core objects for a library system showing attributes (state) and methods (behavior). Notice how relationships emerge naturally from the objects.
Examples
Example 1: E-Commerce Shopping Cart
Problem Statement: “Design a shopping cart system where customers can add products, update quantities, and checkout. Each product has a name, price, and stock quantity.”
Step 1: Extract Nouns Nouns: shopping cart, system, customers, products, quantities, checkout, name, price, stock quantity
Step 2: Filter and Identify Core Objects
- ShoppingCart: Manages items being purchased
- Customer: The person shopping
- Product: Items available for purchase
- ❌ System: That’s what we’re building, not an object
- ❌ Name, price, quantity: These are attributes, not objects
Step 3: Define Attributes and Behaviors
class Product:
"""Represents an item available for purchase."""
def __init__(self, product_id, name, price, stock_quantity):
self.product_id = product_id
self.name = name
self.price = price
self.stock_quantity = stock_quantity
def is_available(self, quantity):
"""Check if requested quantity is in stock."""
return self.stock_quantity >= quantity
def reduce_stock(self, quantity):
"""Reduce stock after purchase."""
if self.is_available(quantity):
self.stock_quantity -= quantity
return True
return False
class Customer:
"""Represents a person shopping on the platform."""
def __init__(self, customer_id, name, email):
self.customer_id = customer_id
self.name = name
self.email = email
self.cart = ShoppingCart(self)
def add_to_cart(self, product, quantity):
"""Add product to customer's cart."""
return self.cart.add_item(product, quantity)
class ShoppingCart:
"""Manages items a customer intends to purchase."""
def __init__(self, customer):
self.customer = customer
self.items = {} # {product_id: (product, quantity)}
def add_item(self, product, quantity):
"""Add or update product quantity in cart."""
if not product.is_available(quantity):
return False
if product.product_id in self.items:
_, current_qty = self.items[product.product_id]
self.items[product.product_id] = (product, current_qty + quantity)
else:
self.items[product.product_id] = (product, quantity)
return True
def calculate_total(self):
"""Calculate total cost of items in cart."""
total = 0
for product, quantity in self.items.values():
total += product.price * quantity
return total
def checkout(self):
"""Process the purchase."""
total = self.calculate_total()
# Reduce stock for each item
for product, quantity in self.items.values():
product.reduce_stock(quantity)
self.items.clear()
return total
# Usage Example
product1 = Product("P001", "Laptop", 999.99, 10)
product2 = Product("P002", "Mouse", 29.99, 50)
customer = Customer("C001", "Alice", "alice@example.com")
customer.add_to_cart(product1, 1)
customer.add_to_cart(product2, 2)
print(f"Cart total: ${customer.cart.calculate_total():.2f}")
# Output: Cart total: $1059.97
total = customer.cart.checkout()
print(f"Purchase complete: ${total:.2f}")
# Output: Purchase complete: $1059.97
print(f"Laptop stock remaining: {product1.stock_quantity}")
# Output: Laptop stock remaining: 9
Key Observations:
- Product has both data (price, stock) and behavior (check availability, reduce stock)
- Customer owns a ShoppingCart — this relationship emerged from the problem
- ShoppingCart manages the collection of items and handles calculations
Try it yourself: Add a remove_item() method to ShoppingCart and a restock() method to Product.
Example 2: Parking Lot System
Problem Statement: “Design a parking lot with multiple levels. Each level has parking spots of different sizes (compact, regular, large). Vehicles of different types (motorcycle, car, bus) need to park in appropriate spots.”
Step 1: Extract Nouns Nouns: parking lot, levels, parking spots, sizes, vehicles, types, motorcycle, car, bus
Step 2: Identify Core Objects
- ParkingLot: The overall facility
- Level: A floor in the parking structure
- ParkingSpot: An individual parking space
- Vehicle: What needs to be parked (abstract concept)
- Motorcycle, Car, Bus: Specific vehicle types
Step 3: Define Attributes and Behaviors
from enum import Enum
from abc import ABC, abstractmethod
class VehicleSize(Enum):
MOTORCYCLE = 1
COMPACT = 2
REGULAR = 3
LARGE = 4
class SpotSize(Enum):
MOTORCYCLE = 1
COMPACT = 2
REGULAR = 3
LARGE = 4
class Vehicle(ABC):
"""Abstract base for all vehicles."""
def __init__(self, license_plate):
self.license_plate = license_plate
self.parking_spot = None
@abstractmethod
def get_size(self):
"""Return the size category of this vehicle."""
pass
class Motorcycle(Vehicle):
def get_size(self):
return VehicleSize.MOTORCYCLE
class Car(Vehicle):
def get_size(self):
return VehicleSize.COMPACT
class Bus(Vehicle):
def get_size(self):
return VehicleSize.LARGE
class ParkingSpot:
"""Represents a single parking space."""
def __init__(self, spot_id, size):
self.spot_id = spot_id
self.size = size
self.vehicle = None
def is_available(self):
"""Check if spot is empty."""
return self.vehicle is None
def can_fit_vehicle(self, vehicle):
"""Check if vehicle size fits this spot."""
return self.size.value >= vehicle.get_size().value
def park_vehicle(self, vehicle):
"""Park a vehicle in this spot."""
if not self.is_available():
return False
if not self.can_fit_vehicle(vehicle):
return False
self.vehicle = vehicle
vehicle.parking_spot = self
return True
def remove_vehicle(self):
"""Remove vehicle from spot."""
if self.vehicle:
self.vehicle.parking_spot = None
self.vehicle = None
class Level:
"""Represents one floor of the parking lot."""
def __init__(self, level_number):
self.level_number = level_number
self.spots = []
def add_spot(self, spot):
"""Add a parking spot to this level."""
self.spots.append(spot)
def park_vehicle(self, vehicle):
"""Try to park vehicle on this level."""
for spot in self.spots:
if spot.is_available() and spot.can_fit_vehicle(vehicle):
if spot.park_vehicle(vehicle):
return spot
return None
def get_available_spots(self):
"""Count available spots."""
return sum(1 for spot in self.spots if spot.is_available())
class ParkingLot:
"""Manages the entire parking facility."""
def __init__(self):
self.levels = []
def add_level(self, level):
"""Add a level to the parking lot."""
self.levels.append(level)
def park_vehicle(self, vehicle):
"""Find a spot for the vehicle across all levels."""
for level in self.levels:
spot = level.park_vehicle(vehicle)
if spot:
return spot
return None
def get_total_available_spots(self):
"""Count all available spots."""
return sum(level.get_available_spots() for level in self.levels)
# Usage Example
parking_lot = ParkingLot()
# Create Level 1 with mixed spot sizes
level1 = Level(1)
level1.add_spot(ParkingSpot("L1-S1", SpotSize.MOTORCYCLE))
level1.add_spot(ParkingSpot("L1-S2", SpotSize.COMPACT))
level1.add_spot(ParkingSpot("L1-S3", SpotSize.REGULAR))
level1.add_spot(ParkingSpot("L1-S4", SpotSize.LARGE))
parking_lot.add_level(level1)
# Park vehicles
motorcycle = Motorcycle("MOTO-123")
car = Car("CAR-456")
bus = Bus("BUS-789")
spot1 = parking_lot.park_vehicle(motorcycle)
print(f"Motorcycle parked at: {spot1.spot_id if spot1 else 'No spot available'}")
# Output: Motorcycle parked at: L1-S1
spot2 = parking_lot.park_vehicle(car)
print(f"Car parked at: {spot2.spot_id if spot2 else 'No spot available'}")
# Output: Car parked at: L1-S2
spot3 = parking_lot.park_vehicle(bus)
print(f"Bus parked at: {spot3.spot_id if spot3 else 'No spot available'}")
# Output: Bus parked at: L1-S4
print(f"Available spots: {parking_lot.get_total_available_spots()}")
# Output: Available spots: 1
Key Observations:
- Vehicle is abstract — we identified that Motorcycle, Car, and Bus share common structure
- ParkingSpot has clear responsibilities: know its size, check availability, manage occupancy
- Level and ParkingLot form a hierarchy that emerged from “multiple levels”
- Each object has a single, clear purpose
Java/C++ Note: In Java, you’d use abstract class Vehicle or interface. In C++, you’d use pure virtual functions: virtual VehicleSize getSize() = 0;
Try it yourself: Add a find_vehicle(license_plate) method to ParkingLot that searches all levels for a specific vehicle.
Common Mistakes
1. Making Everything a Class
Mistake: Creating classes for simple attributes like Title, Price, or Color.
# ❌ Over-engineering
class Title:
def __init__(self, value):
self.value = value
class Book:
def __init__(self, title):
self.title = Title(title) # Unnecessary wrapper
Why it’s wrong: Not everything needs to be an object. Simple data types (strings, numbers) are fine as attributes. Only create a class when the entity has meaningful behavior or complex state.
Fix: Use primitive types for simple attributes:
# ✅ Appropriate design
class Book:
def __init__(self, title):
self.title = title # Just a string
2. Confusing the System with an Object
Mistake: Creating a class for the system itself, like LibrarySystem or ShoppingCartSystem, and putting all logic there.
# ❌ God object anti-pattern
class LibrarySystem:
def __init__(self):
self.books = []
self.members = []
def checkout_book(self, book_id, member_id):
# All logic in one place
pass
Why it’s wrong: This creates a “god object” that does everything. The system is what you’re building, not an object within it. Your objects should interact with each other, not through a central controller.
Fix: Distribute responsibilities to the actual entities:
# ✅ Objects with clear responsibilities
class Member:
def checkout_book(self, book):
if book.is_available():
self.borrowed_books.append(book)
book.checkout()
3. Missing Core Objects
Mistake: Failing to identify important entities that represent relationships or transactions.
# ❌ Missing the Loan object
class Book:
def __init__(self):
self.borrowed_by = None # Just storing member
self.due_date = None # Loose data
Why it’s wrong: When a Book is borrowed by a Member, that relationship has its own data (due date, fine amount) and behavior (calculate fine, extend loan). This deserves its own object.
Fix: Create objects for significant relationships:
# ✅ Loan as a first-class object
class Loan:
def __init__(self, book, member, due_date):
self.book = book
self.member = member
self.due_date = due_date
def calculate_fine(self):
# Behavior belongs to the relationship
pass
4. Focusing Only on Attributes, Ignoring Behavior
Mistake: Identifying objects but only listing their data, creating “anemic” objects.
# ❌ Anemic object (just data)
class Product:
def __init__(self, name, price, stock):
self.name = name
self.price = price
self.stock = stock
# No methods — just a data container
Why it’s wrong: Objects should encapsulate both data and the operations on that data. If your object has no methods, it’s not really an object — it’s just a struct.
Fix: Add behavior that operates on the object’s state:
# ✅ Object with behavior
class Product:
def __init__(self, name, price, stock):
self.name = name
self.price = price
self.stock = stock
def is_available(self, quantity):
return self.stock >= quantity
def reduce_stock(self, quantity):
if self.is_available(quantity):
self.stock -= quantity
return True
return False
5. Creating Too Many Small Objects Too Early
Mistake: Breaking down into tiny objects before understanding the core structure.
# ❌ Premature decomposition
class Street:
pass
class City:
pass
class ZipCode:
pass
class Address:
def __init__(self):
self.street = Street()
self.city = City()
self.zip = ZipCode()
Why it’s wrong: Start with core objects first. You can always refactor later. Over-decomposing early leads to complexity without clarity.
Fix: Start simple, refactor when needed:
# ✅ Start with essential structure
class Address:
def __init__(self, street, city, zip_code):
self.street = street
self.city = city
self.zip_code = zip_code
# Refactor to objects later if behavior emerges
Interview Tips
1. Talk Through Your Process Out Loud
Do this: “Let me start by identifying the main entities. I see ‘user,’ ‘post,’ and ‘comment’ in the problem. Let me list what each needs to know and do…”
Why: Interviewers want to see your thought process. Verbalizing shows you’re systematic, not just guessing. Even if you make a mistake, they can guide you if they understand your reasoning.
2. Ask Clarifying Questions About Entities
Do this: “Should a ‘Reservation’ be its own object, or just an attribute of ‘Room’? Does it need to track history or calculate fees?”
Why: This shows you understand that design decisions depend on requirements. It also prevents you from over-engineering or under-engineering. Interviewers appreciate when you validate assumptions.
3. Use the “Has-A” and “Does-A” Test
Do this: For each potential object, ask:
- “What does this HAVE?” (attributes)
- “What does this DO?” (methods)
If you can’t answer both, it might not be a core object.
Example: “A Book HAS a title, author, ISBN. A Book DOES get checked out, returned, reserved. So Book is definitely a core object.”
Why: This gives you a quick filter and demonstrates structured thinking.
4. Start with 3-5 Core Objects, Then Expand
Do this: “I’ll start with User, Post, and Comment as my core objects. Once we have those defined, we can add Like or Notification if needed.”
Why: Interviewers want to see you prioritize. Starting with too many objects shows poor judgment. Starting with core entities shows you understand what’s essential versus what’s nice-to-have.
5. Draw It Out
Do this: Sketch boxes for each object with attributes and methods listed. Draw arrows showing relationships.
Why: Visual representation helps both you and the interviewer stay aligned. It’s easier to spot missing objects or awkward relationships when you see them on paper/whiteboard.
6. Watch for Relationship Objects
Do this: “Wait — when a User enrolls in a Course, that enrollment has its own data like enrollment date and grade. Should Enrollment be its own object?”
Why: Many candidates miss objects that represent relationships or transactions (Order, Reservation, Enrollment, Loan). Identifying these shows advanced thinking.
7. Validate Your Objects Against Use Cases
Do this: “Let me check: if a user wants to checkout a book, I need Book.checkout() and Member.borrow(). That works. If we need to calculate fines, where does that logic go? Maybe we need a Loan object…”
Why: Walking through scenarios helps you catch missing objects or misplaced responsibilities. It shows you’re thinking about actual usage, not just abstract structure.
8. Don’t Overthink Primitives
Do this: “I’ll use a string for email address. If we need validation later, we can refactor to an Email class.”
Why: Interviewers don’t want to see you create classes for every single attribute. Show pragmatism — start simple, mention where you’d refactor if requirements grew.
Common Interview Question Patterns
- “Design a parking lot” → Look for: ParkingLot, Level, ParkingSpot, Vehicle (and subtypes)
- “Design a library system” → Look for: Book, Member, Loan (not just Book and Member!)
- “Design an online store” → Look for: Product, Customer, ShoppingCart, Order (Order is often missed)
- “Design a social media feed” → Look for: User, Post, Comment, Like (Like might be its own object if it has timestamps, etc.)
The pattern: Always look for the entities (nouns), the relationships between them (often objects themselves), and the transactions/events (also often objects).
Key Takeaways
-
Core objects are the main entities in your system that have both state (attributes) and behavior (methods). Extract them by identifying nouns in the problem statement, then filtering for entities that need to do something or have something done to them.
-
Use a three-step process: (1) Extract nouns from requirements, (2) Identify attributes (what the object knows), (3) Identify behaviors (what the object does). Not every noun becomes a class — filter out primitives, attributes disguised as nouns, and the system itself.
-
Relationships and transactions often need their own objects. When two entities interact (User borrows Book), that relationship may have its own data and behavior (Loan with due date and fine calculation). Don’t miss these.
-
Start with 3-5 core objects and expand. Prioritize the essential entities first, then add supporting objects as needed. This shows good judgment and prevents over-engineering early in the design process.
-
In interviews, verbalize your process and validate with use cases. Talk through how you’re identifying objects, ask clarifying questions, and walk through scenarios to ensure your objects can handle the required functionality. Drawing a diagram helps both you and the interviewer stay aligned.