Loading content...
Every software project begins the same way: with requirements. These might come as formal specification documents, user stories scribbled on sticky notes, verbal descriptions from stakeholders, or even a single sentence on a feature request. Yet the journey from these requirements to working object-oriented code remains one of the most challenging leaps in software engineering.
The fundamental question: How do you look at a problem description—often vague, incomplete, and laden with domain jargon—and see the objects that will form the backbone of your solution?
This skill doesn't come naturally. Most developers, when given a new problem, instinctively think about what the code should do—the sequence of operations, the flow of data, the algorithms to apply. This procedural mindset served us well when learning to code, but it's insufficient for building complex, maintainable systems.
By the end of this page, you will understand how to shift your mental approach when reading requirements—from asking 'What should the program do?' to 'What entities exist in this problem domain, and how do they interact?' This shift is the foundation of all object-oriented design.
Before we can adopt an object-oriented lens, we need to understand what we're moving away from. Most programmers initially learn to think procedurally—breaking problems into sequences of steps, functions that transform inputs to outputs, and flows of control.
Consider this simple requirement:
"The system should allow customers to place orders for products. When an order is placed, the system should calculate the total price including applicable discounts and taxes, then send a confirmation email to the customer."
The procedural approach reads this and immediately thinks:
12345678910
function placeOrder(customerId, productIds, quantities): 1. Fetch customer from database 2. Fetch products from database 3. Calculate subtotal by multiplying prices × quantities 4. Look up applicable discounts 5. Apply discounts to subtotal 6. Calculate tax based on customer location 7. Create order record in database 8. Send email to customer 9. Return order confirmationThis is a valid way to think about the problem, but it immediately couples us to a specific flow. What happens when:
The procedural approach leads to increasingly complex conditional logic, God functions that know everything, and tight coupling between unrelated concerns.
The object-oriented approach reads the same requirement and asks different questions:
Procedural thinking asks: 'What steps solve this problem?' Object-oriented thinking asks: 'What things exist, and how do they collaborate to solve this problem?' The first leads to algorithms; the second leads to architecture.
An object-oriented lens is a mental framework for interpreting problem statements. It's a deliberate way of reading and analyzing requirements that surfaces the objects, relationships, and behaviors hidden within the text.
Think of it like putting on a special pair of glasses. Without them, a requirements document is just text describing what a system should accomplish. With the OO lens, the same text reveals a cast of characters (objects), their attributes (properties), their abilities (methods), and their relationships (associations).
This lens operates through several key principles:
| Principle | Description | Example Application |
|---|---|---|
| Entity Focus | Look for 'things' that exist in the problem domain, not just actions to perform | In 'customers place orders,' see Customer and Order as entities |
| Responsibility Assignment | Ask what each entity should 'know' and what it should 'do' | An Order should know its items; a Customer should know their contact preferences |
| Relationship Mapping | Identify how entities connect, depend on, or contain each other | A Customer 'has' many Orders; an Order 'contains' OrderItems |
| Behavior Encapsulation | Expect entities to manage their own behaviors, not have behaviors imposed externally | An Order calculates its own total, rather than a separate function calculating it |
| Domain Language | Use the vocabulary from the problem domain, not implementation terms | Say 'Cart' not 'ItemList'; say 'checkout' not 'processPayment' |
The power of this lens is that it forces you to think about the structure of a problem before thinking about its solution. This upfront investment in understanding pays dividends:
Reading requirements with an OO lens isn't passive. It's an active, analytical process where you engage with the text, question assumptions, and extract structure from prose. Here's a systematic approach:
Requirements often omit entities that are obvious to domain experts. When you read 'calculate the total price,' there's an implicit concept of a Price or calculation strategy. When you read 'send notification,' there's an implicit Notification and NotificationChannel. Part of the OO lens is surfacing these hidden entities.
Let's apply this to a concrete example. Consider this parking lot system requirement:
"The parking lot management system tracks the occupancy of a multi-floor parking garage. Each floor has multiple parking spots of different sizes (compact, regular, large) for different vehicle types (motorcycles, cars, trucks). The system records when vehicles enter and exit, calculates parking fees based on duration, and displays available spots on each floor."
Analysis through the OO lens:
Notice how active reading reveals entities that weren't explicitly stated but are essential for a working system.
A critical skill when reading with an OO lens is distinguishing between domain objects (entities that exist in the problem space) and implementation details (technical constructs that exist only in the solution space).
Domain objects are concepts that stakeholders understand. If you explained your design to a business analyst or end user, they should recognize these entities. Implementation details are internal mechanics that support the domain objects but don't directly map to real-world concepts.
| Domain Objects | Implementation Details |
|---|---|
| Customer, Order, Product | CustomerRepository, OrderFactory, ProductCache |
| Payment, Invoice, Receipt | PaymentGateway, DatabaseConnection, JSONSerializer |
| Booking, Room, Guest | BookingValidator, RoomAvailabilityIndex, GuestSession |
| Message, Conversation, User | MessageQueue, ConversationCache, UserAuthenticator |
Why does this distinction matter?
When reading requirements, you should focus primarily on domain objects. Implementation details come later, during detailed design and coding. Mixing them up leads to several problems:
When you read 'store order information,' don't immediately think 'OrderDatabase' or 'OrderTable.' Think 'Order' and 'OrderRepository' separately—one is the domain concept, the other is how we persist it. Keep them conceptually distinct.
The test is simple: If you're explaining your design to a non-technical stakeholder, would they recognize this object? If yes, it's probably a domain object. If they'd look at you puzzled, it's probably an implementation detail.
Domain objects should speak the language of the business:
Over time, you'll recognize patterns in how requirements are phrased. These patterns reliably signal specific object-oriented structures. Learning to recognize them accelerates your ability to see the OO structure hidden in prose.
| Requirement Pattern | What It Suggests | Design Implication |
|---|---|---|
| "Users can be admins or regular users" | Specialization hierarchy | Consider inheritance or role-based design |
| "Each order has multiple items" | One-to-many relationship | Order contains a collection of OrderItem |
| "Products belong to categories" | Classification relationship | Product references Category; consider many-to-many |
| "Customers can have VIP status" | State or role | Could be a flag, enum, or separate decorator/strategy |
| "System notifies users via email, SMS, or push" | Strategy/polymorphism | NotificationChannel interface with multiple implementations |
| "Price is calculated based on rules" | Strategy or specification pattern | PricingStrategy or Rule objects |
| "Workflow moves through pending, approved, rejected" | State machine | State pattern or explicit status field with transition rules |
| "System logs all changes" | Audit trail | Separate audit/history entities or event sourcing |
| "Users can undo their actions" | Command pattern | Command objects with reverse operations |
Initially, recognizing these patterns requires conscious effort. With practice, it becomes automatic. You'll read 'The system should allow different payment methods' and immediately think 'PaymentStrategy interface'—before you even finish the sentence.
Watch for these revealing words:
Real-world requirements are rarely crisp and complete. They're often vague, contradictory, or silent on crucial details. Reading with an OO lens means not just extracting what's there, but recognizing what's missing and knowing how to proceed.
Types of requirement ambiguity:
How to handle ambiguity:
Document your assumptions explicitly. When the requirement says 'customers have addresses,' write down 'Assumed: Customer has multiple addresses with one marked primary.' This creates a paper trail and invites correction.
Ask clarifying questions before design. Don't guess when you can ask. Prepare specific, concrete questions: 'Can a product belong to multiple categories?' is better than 'Tell me more about products.'
Design for flexibility when truly uncertain. If you genuinely can't determine whether customers have one or many addresses, design for many—it's easier to constrain later than to expand.
Create multiple candidate designs. Sometimes ambiguity suggests multiple valid interpretations. Sketch alternatives and present tradeoffs to stakeholders.
Use domain experts. The people who will use the system often know things that weren't captured in requirements. A 30-minute conversation with an actual user can resolve hours of speculation.
Don't be frustrated by incomplete requirements—this is the norm, not the exception. Part of professional software engineering is navigating uncertainty intelligently. The OO lens helps by giving you a structured way to identify what you don't know and seek clarification.
Let's walk through a complete example of reading requirements with an OO lens. Here's a partial requirement for a library management system:
"The library system manages a collection of books that members can borrow. Each book has a title, author, and ISBN. Members register with the library by providing their name, email, and a valid ID. A member can borrow up to 5 books at a time for a period of 14 days. If books are returned late, a fine of $0.50 per day is charged. The system should track which books are currently available and allow librarians to add new books or remove damaged ones."
Let's apply our active reading approach:
First Pass Analysis:
Core purpose: Managing book lending in a library.
Key entities mentioned explicitly:
Key processes:
Second Pass — Extracting Objects:
Critical OO Insight:
The requirement says 'books' but likely means two distinct concepts:
A library might have 3 copies of 'Clean Code.' The Book is one entity (Clean Code, Robert Martin, ISBN X); the BookCopies are three entities that can be independently borrowed.
Third Pass — Behaviors and Relationships:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
class Book: - title: String - author: String - isbn: String - copies: List<BookCopy> + isAvailable(): boolean // Any copy available? + getAvailableCopies(): List<BookCopy> class BookCopy: - book: Book - status: Enum (AVAILABLE, BORROWED, DAMAGED) - currentLoan: Loan? + borrow(member: Member): Loan + return(): void + markDamaged(): void class Member: - name: String - email: String - memberId: String - currentLoans: List<Loan> + canBorrow(): boolean // < 5 books + borrowBook(copy: BookCopy): Loan + returnBook(loan: Loan): Fine? class Loan: - member: Member - bookCopy: BookCopy - borrowDate: Date - dueDate: Date - returnDate: Date? + isOverdue(): boolean + calculateFine(): decimal + complete(returnDate: Date): void class Fine: - loan: Loan - amount: decimal - paid: boolean + pay(): void class Library: - books: List<Book> - members: List<Member> - loans: List<Loan> + registerMember(...): Member + addBook(...): Book + removeBook(copy: BookCopy): void + findAvailableBooks(): List<Book>Notice how a short paragraph of requirements yielded a rich object model with 6 classes, clear attributes, defined behaviors, and explicit relationships. This is the power of reading with an OO lens—you don't just understand what the system does; you see the structure of the solution.
Reading requirements with an OO lens is a fundamental skill that improves with deliberate practice. Here's what we've covered:
What's Next:
Now that you understand how to approach requirements with an OO mindset, the next page dives deeper into the core technique: identifying nouns as potential objects. You'll learn systematic methods for extracting candidate classes from any problem statement and how to filter the noise from the signal.
You now understand how to read requirements through an object-oriented lens—seeing entities, relationships, and behaviors rather than just procedures. Next, we'll develop specific techniques for extracting objects from the nouns in problem statements.