Chapter 2
Object-Oriented Paradigm and Domain Modeling
Bachelors Program in Computer Science Engineering
Object-Oriented Programming (C++)
•
Bachelor
Object-Oriented Paradigm and Domain Modeling
Introduction
Object-oriented programming (OOP) is a way of understanding and constructing software as a system of collaborating objects. An object combines data that represents its condition with operations that define what it can do. C++ supports OOP through classes, objects, access control, inheritance, virtual functions, templates, and related language mechanisms.
OOP is not merely a collection of syntax rules. It is a design paradigm. A well-designed program begins by identifying concepts in the problem domain, assigning responsibilities to software objects, and defining clear relationships among them. C++ code is then used to implement that model while managing issues such as ownership, lifetime, type safety, performance, and maintainability.
A library system illustrates the approach. The domain contains books, physical copies, members, loans, and reservations. A book is an intellectual work; a copy is a particular physical or electronic instance. A loan records the borrowing of one copy by one member during a period. This distinction is important: domain modeling must represent meanings and constraints accurately, rather than merely listing nouns.
Learning Objectives
By the end of this chapter, you should be able to:
define object, class, state, behavior, identity, abstraction, and encapsulation;
explain inheritance and polymorphism and distinguish subtype relationships from code reuse;
compare procedural, object-oriented, generic, and functional programming styles;
translate requirements into actors, use cases, responsibilities, and domain classes;
represent multiplicity and association semantics correctly;
evaluate basic C++ designs for cohesion, coupling, extensibility, and safety.
1. The Fundamental Object Model
Objects
An object is a distinguishable entity with:
State: values of its attributes at a particular time;
Behavior: operations it can perform or services it provides;
Identity: a property by which it remains distinguishable from other objects, even when values are equal.
For example, two library copies may both have status “available,” but they are still different copies because their barcodes differ. Two Book objects with identical titles and authors may represent the same work or two separate records; the domain model must state which interpretation applies.
Classes
A class is a type definition describing a family of objects. It specifies data members, member functions, access permissions, invariants, and often ownership relationships. An object is an instance of a class.
The class describes the structure and permitted operations; each constructed Book has its own state. In production code, constructors should establish valid state, and operations should preserve class invariants.
State, behavior, and identity
State is not simply “all variables.” It is the semantically relevant condition of an object. A Loan might have a checkout date and a return date. Its state can change when the copy is returned. Behavior is preferably expressed through meaningful operations such as returnCopy() rather than unrestricted setters.
Identity is particularly important when objects have life histories or relationships. A Copy identified by barcode C104 remains that copy even when its availability changes. Equality may mean identity equality, value equality, or domain equality. These meanings should not be assumed to be interchangeable.
Abstraction
Abstraction focuses attention on essential properties while suppressing irrelevant implementation detail. A LibraryCatalog may offer findByIsbn() without exposing whether it uses a tree, hash table, database, or remote service.
Good abstraction is selective, not vague. It should expose enough information for clients to use an object correctly and conceal details whose variation should not affect clients.
Encapsulation
Encapsulation combines representation and operations while controlling access to the representation. In C++, private data and public member functions are common mechanisms, but encapsulation is a design principle rather than a keyword.
A class should protect its invariants. For example, a loan should not permit a return date earlier than its checkout date. Rather than exposing returnDate_ for arbitrary modification, the class can provide a validated operation such as returnCopy(date).
Encapsulation also reduces coupling. If clients depend only on stable operations, the implementation can change without requiring widespread edits.
2. Relationships Among Classes
Association
An association indicates that objects know about, use, or are connected to one another. Multiplicity states how many instances may participate at each end. Common multiplicities include:
Notation
Meaning
1
exactly one
0..1
zero or one
or 0..
zero or more
1..*
one or more
Multiplicity is a structural constraint, not a status label. In a library, a Loan refers to exactly one Copy, whether the loan is open or closed. A Copy can be associated with zero or many historical Loan records over time. A Book may have zero or many physical Copy objects, while each copy represents one book in this model.
Library domain model showing books, copies, members and loans with multiplicities
Figure: Library domain model showing books, copies, members and loans with multiplicities
Figure: A library domain model. One book may have zero or many copies; each copy belongs to one book. A copy may have many historical loans; each loan concerns one copy and one member, regardless of whether the loan is open or closed.
Aggregation and composition
Aggregation suggests a whole–part relationship in which parts may exist independently. Composition is stronger: the whole controls the lifetime or ownership of its parts. For example, an Order may compose its OrderLine objects if lines have no meaningful existence outside that order. A Book and a Copy are often modeled as an association rather than composition because inventory records may be archived independently.
These distinctions depend on domain rules. They should not be inferred solely from programming convenience.
Generalization and dependency
Generalization represents an “is-a” relationship. A PrintedBook may be a specialized kind of Book if it satisfies the behavioral contract of Book. Dependency indicates temporary use, such as a report generator depending on a Catalog interface.
3. Inheritance and Polymorphism
Inheritance allows a derived class to acquire or specialize members of a base class. Public inheritance in C++ should normally express substitutability: code expecting a base object should be able to use a derived object without violating the base class’s contract.
Polymorphism means that one interface can represent multiple concrete forms. With a virtual function, a call through a base pointer or reference is dynamically dispatched to the appropriate override. The base destructor is virtual because objects may be destroyed through a base pointer.
Inheritance is not automatically the best reuse mechanism. Prefer composition when a class merely uses another object, when behavior changes independently, or when the subtype relationship is artificial. A LibraryService that contains a Catalog usually has a “has-a” relationship, not an “is-a” relationship.
4. Programming Paradigms Compared
Style
Primary abstraction
Strength
Typical risk
Procedural
procedures acting on data
direct control and simple algorithms
data invariants become widely exposed
Object-oriented
collaborating objects
locality of responsibility and extensibility
excessive hierarchy or mutable coupling
Generic
algorithms parameterized by types or policies
reuse across types with compile-time checking
difficult constraints or error messages
Functional
expressions, functions, and transformations
predictable reasoning and reduced side effects
awkwardness for inherently stateful systems
These styles are not mutually exclusive. Modern C++ is multi-paradigm. A domain class may encapsulate state, a generic algorithm may process a range, and a lambda may express a transformation.
For example, std::sort is a generic algorithm, but it does not operate on every standard container. It requires a range whose iterators satisfy random-access iterator requirements; std::vector and std::deque qualify, whereas std::list does not. A list can instead use its member function list::sort(), or data can be copied into a random-access container when appropriate.
Functional techniques such as pure helper functions and immutable values can improve testability. Procedural code remains suitable for small computations or performance-critical loops. The design question is not which paradigm is universally superior, but which abstraction best matches the problem and its constraints.
5. From Requirements to a Domain Model
Domain modeling translates stakeholder language into a structured conceptual representation. It should precede detailed implementation, but it is iterative rather than mechanical.
Step 1: Identify stakeholders and goals
Stakeholders include borrowers, librarians, administrators, and external notification services. Their goals become candidate use cases, such as “search catalog,” “check out copy,” “return copy,” and “renew loan.”
Step 2: Write use cases
A use case describes a goal-oriented interaction, not a screen or class. A concise use case contains:
actor and goal;
preconditions;
main success scenario;
alternative or exceptional flows;
postconditions.
Use case: Check out a copy
Actor: librarian or authorized self-service member.
Preconditions: member is eligible; copy is available.
Main flow: identify member, identify copy, validate policy, create loan, mark copy unavailable, issue confirmation.
Alternatives: member has reached the borrowing limit; copy is reserved; member account is blocked.
Postconditions: one loan exists for the copy and member, and the copy’s current availability reflects the transaction.
A use case may lead to several collaborating objects. The actor does not necessarily correspond to a software class; an external person can remain an actor.
Step 3: Extract candidate concepts
Nouns provide candidates such as Member, Copy, Loan, and Reservation. Verbs suggest responsibilities such as checkOut, returnCopy, renew, and search. Candidates must be tested against domain meaning, lifecycle, and information requirements.
Step 4: Assign responsibilities
A responsibility includes knowledge or behavior. A Loan can know its checkout and due dates and determine whether it is overdue. A CirculationService may coordinate a checkout because the operation changes several objects and applies policy. A Copy can answer whether it is available, but a service may decide whether a particular member is eligible.
The principle of high cohesion suggests keeping closely related responsibilities together. Low coupling suggests minimizing unnecessary knowledge of other objects.
Step 5: State invariants and constraints
Examples include:
every loan refers to exactly one copy and one member;
a copy may have many historical loans but at most one active loan at a time;
a returned loan still retains its copy association;
a copy cannot be checked out if it is already on an active loan;
an ISBN identifies a work according to the system’s chosen identity policy.
Notice the distinction between association multiplicity and temporal business rules. “At most one active loan” is a constraint on current state; it does not change the historical association from Copy to Loan.
Step 6: Validate scenarios
Walk through normal and exceptional scenarios. Ask which object receives each message, what state changes, and whether the model can enforce the stated rules. If an object becomes a passive data container while a controller performs all logic, the allocation of responsibilities should be reconsidered.
6. Translating the Model into C++
A conceptual class need not map one-to-one to a C++ class. Some domain concepts become value types, some become entities with identity, and some become services or interfaces.
Value object: defined primarily by value, such as a date or ISBN wrapper.
Entity: has a stable identity and lifecycle, such as a member or copy.
Service: provides an operation that does not naturally belong to one entity, such as a circulation policy service.
Repository: abstracts persistence and retrieval.
Use strong types when they prevent invalid combinations. For example, a MemberId type is safer than passing an arbitrary string wherever an identifier is expected. Use const to communicate non-modification and RAII objects to manage resources. Prefer standard library containers and algorithms where their requirements match the task.
Ownership must be explicit. A std::unique_ptr generally expresses exclusive ownership; std::shared_ptr expresses shared ownership but should not be used merely because pointers are involved. Non-owning references or pointers require a lifetime guarantee. Cyclic ownership through shared_ptr can prevent destruction; std::weak_ptr may break such a cycle, although redesign is often preferable.
7. Case Study: Illustrative Library Circulation Design
Background
A university library wants to replace a spreadsheet-based circulation process. The system must track works, physical copies, members, reservations, and historical loans.
Problem
The spreadsheet conflates a book title with a physical copy and overwrites return information. It cannot reliably answer which copy is currently borrowed, or produce an accurate loan history.
Analysis
The key modeling decision is to distinguish Book from Copy. Book represents bibliographic identity and may have many copies. Copy represents a lendable item with a barcode. Loan represents an event or record connecting one copy and one member. A loan may be open or closed, but closure does not remove its historical associations.
The model also distinguishes current availability from loan history. A copy’s current status can be derived from active loans or maintained as a carefully synchronized state. If both are stored, transactions must preserve consistency.
Approach
A possible design contains:
Book: title, authors, ISBN, and its copy relationship;
Copy: barcode, condition, and availability operations;
Member: identity, eligibility information, and borrowing history;
Loan: checkout date, due date, optional return date, one copy, and one member;
CirculationService: policy validation and coordination of checkout and return;
CatalogRepository and LoanRepository: persistence abstractions.
The checkout operation validates member eligibility, confirms that the copy has no active loan, creates a loan, and records the transaction atomically. The return operation closes the existing loan and updates current availability. It does not delete the loan or sever its historical references.
Outcome
The design can answer both current and historical questions: “Is copy C104 available?” and “Which loans previously involved C104?” It also permits multiple copies of the same book and supports policy changes without embedding all policy logic in user-interface code.
Learning points
The most important result is not the number of classes but the accuracy of the model. Correct multiplicities, explicit invariants, and well-allocated responsibilities prevent ambiguity that would otherwise appear as data errors and maintenance costs.
8. Design Quality, Limitations, and Professional Practice
OOP can improve changeability, but it does not guarantee good software. Common failures include “god classes” with excessive responsibilities, deep inheritance trees, getters and setters that expose all representation, and models that mirror database tables without representing behavior.
Evaluate a design using questions such as:
Is each class cohesive?
Are dependencies directed toward stable abstractions?
Can invariants be enforced locally?
Does inheritance represent a valid substitutable relationship?
Are ownership and object lifetimes unambiguous?
Can behavior be tested without requiring the entire infrastructure?
Security and governance also matter. A member’s borrowing history may be personal data and should be protected through access control, retention policies, auditing, and appropriate legal compliance. The model should separate authorization from ordinary domain data and avoid exposing sensitive details through diagnostic output.
Contemporary C++ development increasingly combines OOP with generic and functional techniques. Concepts can constrain templates; ranges express composable processing; smart pointers and value semantics reduce lifetime errors; static analysis and automated tests detect violations early. AI-assisted code generation can accelerate prototyping, but generated designs must be reviewed for ownership errors, incorrect assumptions, security weaknesses, licensing concerns, and failure to preserve domain invariants.
Chapter Summary
Object-oriented design models software as interacting objects with state, behavior, and identity. Classes define object structure and contracts; abstraction reduces irrelevant detail, while encapsulation protects representation and invariants. Associations, multiplicities, aggregation, composition, generalization, and dependencies describe relationships among concepts.
Inheritance should express a genuine substitutable relationship, and polymorphism permits different implementations behind a common interface. Procedural, object-oriented, generic, and functional styles are complementary tools in C++. Generic algorithms have requirements: for example, std::sort requires random-access iterator ranges and therefore does not support every standard container.
Domain modeling begins with stakeholders and use cases, then identifies concepts, assigns responsibilities, states invariants, and validates scenarios. In the library case, a book may have many copies, a copy may have many historical loans, and each loan refers to one copy and one member whether open or closed. Accurate modeling of these distinctions is essential for reliable C++ systems.
Key Terms
Object: A distinguishable entity with state, behavior, and identity.
Class: A type definition describing objects and their permitted operations.
State: The semantically relevant condition of an object at a time.
Behavior: Operations or services an object provides.
Identity: The property that distinguishes an object from others.
Abstraction: Representation of essential features while hiding irrelevant detail.
Encapsulation: Protection and organization of representation through a controlled interface.
Invariant: A condition that must remain true for a valid object or system.
Association: A meaningful relationship between classes or objects.
Multiplicity: The permitted number of related instances at an association end.
Aggregation: A weak whole–part relationship.
Composition: A strong whole–part relationship involving ownership or lifetime.
Inheritance: Definition of a class in terms of a more general class.
Polymorphism: Use of one interface for multiple concrete forms.
Substitutability: The ability to use a subtype where its base type is expected.
Use case: A goal-oriented interaction between an actor and a system.
Responsibility: Knowledge or behavior assigned to a design element.
Entity: An object whose identity and lifecycle are significant.
Value object: An object primarily defined by its value.
Cohesion: Degree to which a class’s responsibilities belong together.
Review Questions
Short Answer Questions
What are the three essential characteristics of an object?
Distinguish abstraction from encapsulation.
What does multiplicity 0..* mean at an association end?
Why should public inheritance normally support substitutability?
What iterator category does std::sort require?
Long Answer Questions
Explain state, behavior, and identity using Book, Copy, and Loan examples.
Compare procedural, object-oriented, generic, and functional programming styles, including one strength and one limitation of each.
Explain how inheritance, composition, and polymorphism differ and when composition may be preferable.
Describe a systematic process for converting requirements into use cases, responsibilities, and a domain model.
Discuss how encapsulation, invariants, ownership, and cohesion contribute to maintainable C++ software.
Analytical / Application Questions
Model a hospital appointment system. Identify at least five domain concepts, their responsibilities, key associations, and two invariants.
A design states that a closed loan has 0..1 copies. Diagnose the modeling error and propose corrected multiplicities and a separate rule for active loans.
A program stores records in std::list and attempts to call std::sort with the list’s iterators. Explain why this is invalid and propose two appropriate alternatives.
Multiple Choice Questions with Answers
Question 1
Which property allows two objects with identical attribute values to remain distinguishable?
A. Identity
B. Inheritance
C. Aggregation
D. Compilation
Correct Answer: A. Identity
Question 2
In the library model, what does Book 1 — Copy 0..* express?
A. A book must always be unavailable
B. One book may have zero or many copies
C. A copy may belong to many books simultaneously
D. A book can have only one historical loan
Correct Answer: B. One book may have zero or many copies
Question 3
A closed loan in the corrected library model should refer to:
A. No copy, because the loan is closed
B. Exactly one copy, because it records historical borrowing
C. Exactly two copies, one before and one after return
D. An optional book but no copy
Correct Answer: B. Exactly one copy, because it records historical borrowing
Question 4
Which mechanism most directly protects a class invariant from arbitrary client modification?
A. Public data members
B. Unrestricted setters
C. Encapsulation with controlled operations
D. Global variables
Correct Answer: C. Encapsulation with controlled operations
Question 5
Public inheritance is most appropriate when:
A. The derived type can substitute for the base type
B. The base class is used only as a namespace
C. Two classes happen to share one data member
D. The derived class must hide every base operation
Correct Answer: A. The derived type can substitute for the base type
Question 6
Which standard container does not provide random-access iterators for its ordinary sequence iteration?
A. std::vector
B. std::deque
C. std::array
D. std::list
Correct Answer: D. std::list
Question 7
What is the primary purpose of a use case?
A. To describe a goal-oriented interaction involving an actor and system
B. To specify the private layout of every class
C. To list compiler optimization flags
D. To replace all domain rules with database tables
Correct Answer: A. To describe a goal-oriented interaction involving an actor and system
Question 8
Which is most naturally a value object?
A. A library member with a persistent account
B. A physical copy with a barcode
C. A date represented by its value
D. A circulation service coordinating transactions
Correct Answer: C. A date represented by its value
Question 9
Which statement best describes composition?
A. A relationship in which parts are always unrelated to a whole
B. A strong whole–part relationship involving ownership or lifetime
C. A synonym for virtual dispatch
D. A generic algorithm requirement
Correct Answer: B. A strong whole–part relationship involving ownership or lifetime
Question 10
Why might a circulation service coordinate checkout rather than assigning all checkout logic to Copy?
A. Checkout may validate policy and change several collaborating objects
B. Copies cannot have any behavior
C. Services are automatically faster than entities
D. C++ forbids member functions in domain classes
Correct Answer: A. Checkout may validate policy and change several collaborating objects