Identifying Objects in Problem Statements
Practicing Object Identification
Read aloud is unavailable in this browser.
Building Fluency Through Practice
Object identification is a skill, and like any skill, it develops through deliberate practice. You've learned the techniques: reading with an OO lens, extracting nouns as candidate objects, mapping verbs to methods, and filtering candidates into a refined class design.
Now it's time to put these techniques into practice. This page presents a series of increasingly complex exercises, each walking you through the complete process from requirements to initial object model. Work through each example actively—don't just read the solutions. Try to identify objects and methods yourself before checking the analysis.
True mastery comes when you can look at any problem statement and see the objects within it, almost effortlessly. That's our goal.
For each exercise: (1) Read the requirements carefully, (2) Extract nouns and verbs yourself, (3) Attempt to identify classes and methods, (4) Then compare your analysis to the provided solution. The goal isn't to match the solution exactly—reasonable designs can vary—but to develop your analytical thinking.
Review: The Complete Process
Before diving into exercises, let's consolidate the complete object identification process:
Object Identification Process
- •Read for understanding — Grasp the domain, core purpose, and scope of the system
- •Extract nouns — Mark all nouns and noun phrases as candidate objects
- •Categorize nouns — Distinguish entities, attributes, roles, and system context
- •Filter to classes — Apply criteria: identity, attributes, behavior, lifecycle
- •Extract verbs — Mark all verbs as candidate methods
- •Assign methods — Use Information Expert to place behaviors on the right classes
- •Identify relationships — Note associations, aggregations, and dependencies
- •Surface implicit entities — Find hidden classes from relationships and events
- •Refine and iterate — Review for completeness, cohesion, and domain alignment
Your first pass won't be perfect. Expect to revisit and refine as you discover new information or recognize patterns you missed. The goal is a coherent model, not a perfect first draft.
Exercise 1: Restaurant Ordering System
Requirements:
"Design a restaurant ordering system where customers can view the menu, place orders, and pay their bill. The menu contains items organized into categories like appetizers, mains, and desserts. Each menu item has a name, description, price, and availability status. Customers can customize orders with special instructions (e.g., 'no onions'). Staff can update item availability and view pending orders. The system tracks orders from placement through preparation to serving."
Pause here and try to identify objects and methods yourself before continuing.
Analysis:
Step 1: Extract Nouns
From the requirements:
- restaurant, ordering system (context)
- customers, staff
- menu, items, categories, appetizers/mains/desserts
- name, description, price, availability status (attributes)
- orders, bill, special instructions
- placement, preparation, serving (states)
Step 2: Categorize and Filter
| Noun | Category | Decision |
|---|---|---|
| restaurant, ordering system | Context | ❌ System boundary |
| customers | Entity | ✅ User type |
| staff | Entity | ✅ User type with admin capabilities |
| menu | Entity | ✅ Contains categories and items |
| items (menu items) | Entity | ✅ Core domain object |
| categories | Entity | ✅ Groups menu items |
| appetizers, mains, desserts | Instances | ❌ Just category examples |
| name, description, price | Attributes | ❌ Properties of MenuItem |
| availability status | Attribute/Enum | ⚠️ Attribute of MenuItem |
| orders | Entity | ✅ Core transactional entity |
| bill | Entity | ⚠️ Possibly Order or OrderPayment |
| special instructions | Attribute | ❌ Property of OrderItem |
| placement, preparation, serving | States | ❌ States of Order (enum or status field) |
Step 3: Extract Verbs
- view (menu)
- place (orders)
- pay (bill)
- customize (orders)
- update (availability)
- view (pending orders)
- track (orders)
Step 4: Assign Methods
| Verb | Method | Assigned To |
|---|---|---|
| view menu | getMenu() or getMenuItems() | Menu or MenuRepository |
| place orders | placeOrder(items) | OrderService or Customer |
| pay bill | pay(amount, method) | Order or PaymentService |
| customize | addSpecialInstructions(text) | OrderItem |
| update availability | setAvailability(status) | MenuItem |
| view pending orders | getPendingOrders() | OrderRepository |
| track | getStatus(), updateStatus() | Order |
Step 5: Final Class Model
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
// Usersclass Customer { id: string; name: string; placeOrder(items: OrderItem[]): Order; viewOrderHistory(): Order[];} class Staff { id: string; name: string; role: StaffRole; updateItemAvailability(item: MenuItem, status: boolean): void; viewPendingOrders(): Order[]; updateOrderStatus(order: Order, status: OrderStatus): void;} // Menu Domainclass Menu { categories: Category[]; getAvailableItems(): MenuItem[]; getItemsByCategory(category: Category): MenuItem[];} class Category { id: string; name: string; displayOrder: number; items: MenuItem[];} class MenuItem { id: string; name: string; description: string; price: Money; category: Category; isAvailable: boolean; setAvailability(available: boolean): void;} // Order Domainclass Order { id: string; customer: Customer; items: OrderItem[]; status: OrderStatus; createdAt: Date; addItem(menuItem: MenuItem, quantity: number): OrderItem; removeItem(orderItem: OrderItem): void; calculateTotal(): Money; updateStatus(status: OrderStatus): void; pay(paymentMethod: PaymentMethod): Payment;} class OrderItem { menuItem: MenuItem; quantity: number; specialInstructions: string; addSpecialInstructions(text: string): void; getSubtotal(): Money;} enum OrderStatus { PLACED, PREPARING, READY, SERVED, PAID, CANCELLED}Notice how 'bill' became part of Order (the pay() method and PAID status) rather than a separate class. Notice also how 'special instructions' lives on OrderItem, not Order—because customizations apply to specific items, not the whole order.
Exercise 2: Airline Booking System
Requirements:
"Design an airline booking system where passengers can search for flights, select seats, and make reservations. Flights have a departure city, arrival city, date, time, and available seats. Seats are classified as economy, business, or first class with different prices. Passengers provide their personal information and contact details during booking. The system generates a confirmation with a booking reference number. Passengers can check-in online 24 hours before departure and receive a boarding pass. Airlines can add, modify, or cancel flights."
Attempt your analysis before reading on.
Analysis:
- •Passenger — Person making booking
- •Flight — Scheduled flight instance
- •Seat — Specific seat on a flight
- •SeatClass — Economy/Business/First (enum or class)
- •Booking/Reservation — The confirmed reservation
- •Confirmation — Implicit in booking confirmation
- •BoardingPass — Generated at check-in
- •Airline — Admin/flight operator
- •City/Airport — Location entities
- •search → FlightSearchService.search()
- •select → Booking.selectSeat()
- •reserve/book → Booking.create()
- •provide info → Part of booking flow
- •generate confirmation → Booking.confirm()
- •check-in → Booking.checkIn()
- •receive boarding pass → Booking.generateBoardingPass()
- •add/modify/cancel flights → Airline operations
Key Design Decisions:
-
Flight vs FlightSchedule: 'Flights' might mean a specific flight on a specific date, or a recurring schedule. We're treating it as a specific flight instance (Flight 123 on Jan 15).
-
Seat belongs to Flight: Each Flight has its own seats with availability. A seat is not a global entity—it's 'Seat 12A on Flight 123 on Jan 15.'
-
Booking captures the relationship: Booking links Passenger to Flight and Seat, and tracks the lifecycle (confirmed → checked-in → boarded).
-
BoardingPass is generated, not created from scratch: It's derived from Booking at check-in time.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
class Flight { flightNumber: string; departureAirport: Airport; arrivalAirport: Airport; departureTime: DateTime; arrivalTime: DateTime; aircraft: Aircraft; seats: Seat[]; status: FlightStatus; getAvailableSeats(seatClass?: SeatClass): Seat[]; isAvailable(): boolean; cancel(): void; delay(newTime: DateTime): void;} class Seat { seatNumber: string; // e.g., "12A" seatClass: SeatClass; // ECONOMY, BUSINESS, FIRST price: Money; isAvailable: boolean; reserve(): void; release(): void;} class Booking { referenceNumber: string; // e.g., "ABC123" passenger: Passenger; flight: Flight; seat: Seat; status: BookingStatus; // CONFIRMED, CHECKED_IN, CANCELLED createdAt: DateTime; static create(passenger: Passenger, flight: Flight, seat: Seat): Booking; confirm(payment: Payment): void; checkIn(): BoardingPass; // Only available 24h before cancel(): CancellationResult; canCheckIn(): boolean;} class BoardingPass { booking: Booking; gate: string; boardingTime: DateTime; barcode: string; static generateFrom(booking: Booking): BoardingPass;} class Passenger { id: string; firstName: string; lastName: string; email: string; phone: string; passportNumber?: string; getBookings(): Booking[]; getUpcomingFlights(): Flight[];} class Airline { code: string; // e.g., "UA" name: string; addFlight(details: FlightDetails): Flight; cancelFlight(flight: Flight): void; modifyFlight(flight: Flight, changes: FlightChanges): void;} class FlightSearchService { search(criteria: FlightSearchCriteria): Flight[];}Aircraft wasn't mentioned but is implied (flights use planes with specific seat configurations). Airport emerged from 'city' but is more precise. Payment is implicit in booking confirmation. These implicit entities complete the model.
Exercise 3: Social Media Platform
Requirements:
"Design a social media platform where users can create profiles, post content, and interact with others. Users can follow other users and see posts from people they follow in their feed. Posts can contain text, images, or videos. Users can like posts, leave comments, and share posts to their own profile. Comments can receive likes and replies. Users can send private messages to each other. The system notifies users of new followers, likes, and comments. Users can block other users, which prevents all interactions."
This is a complex system. Try identifying the core entities and their relationships.
Analysis:
Identified Classes:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
// User Domainclass User { id: string; username: string; profile: Profile; followers: User[]; following: User[]; blockedUsers: User[]; createPost(content: PostContent): Post; follow(user: User): void; unfollow(user: User): void; block(user: User): void; unblock(user: User): void; isBlocking(user: User): boolean; getFeed(): Post[]; // Posts from followed users sendMessage(to: User, content: string): Message;} class Profile { displayName: string; bio: string; profileImage: Media; coverImage: Media; update(details: ProfileDetails): void;} // Content Domainclass Post { id: string; author: User; content: PostContent; likes: Like[]; comments: Comment[]; shares: Share[]; createdAt: DateTime; visibility: PostVisibility; like(user: User): Like; unlike(user: User): void; addComment(user: User, text: string): Comment; share(user: User): Share; getLikeCount(): number; isLikedBy(user: User): boolean;} class PostContent { text: string; media: Media[]; // Images or videos} class Media { type: MediaType; // IMAGE, VIDEO url: string; thumbnailUrl: string;} class Comment { id: string; author: User; post: Post; parentComment?: Comment; // For replies text: string; likes: Like[]; createdAt: DateTime; reply(user: User, text: string): Comment; like(user: User): Like; unlike(user: User): void;} class Like { user: User; target: Post | Comment; createdAt: DateTime;} class Share { user: User; originalPost: Post; comment?: string; // Optional share caption createdAt: DateTime;} // Messaging Domainclass Message { id: string; sender: User; recipient: User; content: string; sentAt: DateTime; readAt?: DateTime; markAsRead(): void; isRead(): boolean;} class Conversation { participants: User[]; messages: Message[]; getLatestMessages(count: number): Message[]; sendMessage(from: User, content: string): Message;} // Notificationsclass Notification { id: string; recipient: User; type: NotificationType; // NEW_FOLLOWER, LIKE, COMMENT, MESSAGE data: NotificationData; createdAt: DateTime; readAt?: DateTime; markAsRead(): void;}| Relationship | Type | Implementation |
|---|---|---|
| User follows User | Many-to-many, self-reference | Following/followers collections |
| User blocks User | Many-to-many, self-reference | BlockedUsers collection |
| User authors Posts | One-to-many | Post.author reference |
| Users like Posts | Many-to-many | Like entity captures who and when |
| Comment replies to Comment | Self-reference, tree structure | parentComment reference |
| Users exchange Messages | Many-to-many | Message entity with sender/recipient |
Notice patterns: Like is an entity (not just a count) because we need to track who liked what and when. Comment uses a self-reference for nested replies. Block checking must happen before allowing interactions—this is a cross-cutting concern.
Exercise 4: Learning Management System
Requirements:
"Design a learning management system where instructors create courses with modules and lessons. Each lesson can contain videos, documents, and quizzes. Students enroll in courses and track their progress through the curriculum. Quizzes have multiple-choice and free-text questions; the system grades multiple-choice automatically but free-text requires instructor review. Students earn certificates upon completing all required lessons. Instructors can view analytics on student performance and engagement."
Analysis:
Core Objects Identified:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
// Course Structureclass Course { id: string; title: string; description: string; instructor: Instructor; modules: Module[]; addModule(module: Module): void; publish(): void; getEnrolledStudents(): Student[]; getCompletionRate(): number;} class Module { id: string; title: string; order: number; lessons: Lesson[]; addLesson(lesson: Lesson): void; reorderLessons(newOrder: string[]): void;} class Lesson { id: string; title: string; order: number; content: LessonContent[]; // Polymorphic isRequired: boolean; addContent(content: LessonContent): void;} // Lesson Content (Strategy/Polymorphism)interface LessonContent { type: ContentType; render(): void;} class VideoContent implements LessonContent { videoUrl: string; duration: number; transcript?: string;} class DocumentContent implements LessonContent { documentUrl: string; pageCount: number;} class Quiz implements LessonContent { questions: Question[]; passingScore: number; grade(submission: QuizSubmission): QuizResult;} // Questions (Polymorphism)abstract class Question { id: string; text: string; points: number; abstract grade(answer: Answer): number; abstract requiresManualGrading(): boolean;} class MultipleChoiceQuestion extends Question { options: string[]; correctAnswer: number; grade(answer: Answer): number { return answer.selectedOption === this.correctAnswer ? this.points : 0; } requiresManualGrading(): boolean { return false; }} class FreeTextQuestion extends Question { gradingRubric?: string; grade(answer: Answer): number { return -1; // Indicates manual grading needed } requiresManualGrading(): boolean { return true; }} // Enrollment and Progressclass Enrollment { student: Student; course: Course; enrolledAt: DateTime; progress: Progress; isCompleted(): boolean; getCompletionPercentage(): number; generateCertificate(): Certificate;} class Progress { completedLessons: Lesson[]; quizResults: QuizResult[]; markLessonComplete(lesson: Lesson): void; hasCompletedLesson(lesson: Lesson): boolean; hasPassedQuiz(quiz: Quiz): boolean;} class Certificate { student: Student; course: Course; issuedAt: DateTime; certificateNumber: string; static generate(enrollment: Enrollment): Certificate; download(): Blob;} // Usersclass Student { id: string; name: string; email: string; enrollments: Enrollment[]; enroll(course: Course): Enrollment; getProgress(course: Course): Progress; getCertificates(): Certificate[];} class Instructor { id: string; name: string; courses: Course[]; createCourse(details: CourseDetails): Course; viewAnalytics(course: Course): CourseAnalytics; gradeSubmission(submission: QuizSubmission, score: number): void;}Notice how Question uses inheritance: MultipleChoiceQuestion auto-grades while FreeTextQuestion requires manual review. LessonContent could be an interface with Video, Document, and Quiz implementations. This is the Strategy pattern enabling extensibility.
Common Mistakes to Avoid
Through these exercises and real-world experience, certain patterns of mistakes emerge. Being aware of them helps you avoid them:
- •Missing reified events — Actions like 'booking,' 'payment,' 'enrollment' are often classes, not just method calls. If you need to track when something happened or its details, reify it.
- •Confusing roles with types — 'Admin user' isn't a separate class from User—it's a role. Don't create CustomerUser and AdminUser; create User with roles.
- •Ignoring lifecycle — If an entity moves through states (pending → active → completed), model those states. State changes often reveal missing methods.
- •Skipping relationship entities — When a relationship has attributes (like enrolled_at for Student-Course), create a junction entity (Enrollment).
- •Over-relying on services — Services should orchestrate, not contain business logic. If your services have all the behavior, your domain model is anemic.
- •Ignoring value objects — Money, DateRange, Address, Email are often better as small, immutable value objects than as primitive fields.
- •Missing polymorphism opportunities — When you see 'different types of X,' consider inheritance or strategy pattern instead of switch statements.
If one class has 20+ methods or knows about everything in the system, it's a 'God Object.' This usually means responsibilities weren't properly distributed. Break it down using the heuristics: what data does each method need? That's where the method should live.
Self-Assessment Checklist
Use this checklist to evaluate your object identification work:
Model Completeness
- •☐ All nouns in requirements are accounted for (as classes, attributes, or deliberately excluded)
- •☐ All verbs are assigned as methods on appropriate classes
- •☐ Implicit entities (events, relationships with data) have been surfaced
- •☐ User types and their different capabilities are modeled
- •☐ State lifecycles are represented (enums, status fields, or state patterns)
Design Quality
- •☐ Each class has a single, clear responsibility
- •☐ Methods are placed with the data they need (Information Expert)
- •☐ No class has excessive dependencies on other classes
- •☐ Domain language is used in class and method names
- •☐ Polymorphism is used where 'different types of' appear
- •☐ Value objects are used for composite, immutable values
Stakeholder Validation
- •☐ A domain expert would recognize the class names
- •☐ The model captures the business rules mentioned in requirements
- •☐ Edge cases and constraints are represented in the model
- •☐ The model supports all use cases described in requirements
KEEP LEARNING
There’s more to understand.
You’re reading the introduction. Create a free account to read the complete lesson, save notes and keep track of your progress.